acts-as-taggable-on 1.0.13 → 2.0.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/CHANGELOG +5 -2
- data/Gemfile +6 -0
- data/README.rdoc +61 -31
- 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 +98 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +237 -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 +64 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +43 -373
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +58 -43
- data/lib/acts_as_taggable_on/compatibility/Gemfile +6 -0
- data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +17 -0
- data/lib/acts_as_taggable_on/tag.rb +47 -8
- data/lib/acts_as_taggable_on/tag_list.rb +45 -45
- data/lib/acts_as_taggable_on/tagging.rb +17 -2
- data/lib/acts_as_taggable_on/tags_helper.rb +8 -2
- data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +31 -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 +98 -53
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +46 -4
- data/spec/acts_as_taggable_on/tag_list_spec.rb +18 -0
- data/spec/acts_as_taggable_on/tag_spec.rb +66 -13
- data/spec/acts_as_taggable_on/taggable_spec.rb +142 -70
- data/spec/acts_as_taggable_on/tagger_spec.rb +73 -5
- data/spec/acts_as_taggable_on/tagging_spec.rb +18 -3
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -3
- data/spec/bm.rb +52 -0
- data/spec/models.rb +30 -0
- data/spec/schema.rb +13 -2
- data/spec/spec.opts +1 -2
- data/spec/spec_helper.rb +39 -34
- metadata +28 -8
- data/lib/acts_as_taggable_on/group_helper.rb +0 -12
- data/spec/acts_as_taggable_on/group_helper_spec.rb +0 -18
|
@@ -2,25 +2,36 @@ require File.dirname(__FILE__) + '/../spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe Tag do
|
|
4
4
|
before(:each) do
|
|
5
|
+
clean_database!
|
|
5
6
|
@tag = Tag.new
|
|
6
|
-
@user = TaggableModel.create(:name => "Pablo")
|
|
7
|
-
Tag.delete_all
|
|
7
|
+
@user = TaggableModel.create(:name => "Pablo")
|
|
8
8
|
end
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
describe "named like any" do
|
|
11
|
+
before(:each) do
|
|
12
|
+
Tag.create(:name => "awesome")
|
|
13
|
+
Tag.create(:name => "epic")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should find both tags" do
|
|
17
|
+
Tag.named_like_any(["awesome", "epic"]).should have(2).items
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
10
21
|
describe "find or create by name" do
|
|
11
22
|
before(:each) do
|
|
12
23
|
@tag.name = "awesome"
|
|
13
24
|
@tag.save
|
|
14
25
|
end
|
|
15
|
-
|
|
26
|
+
|
|
16
27
|
it "should find by name" do
|
|
17
28
|
Tag.find_or_create_with_like_by_name("awesome").should == @tag
|
|
18
29
|
end
|
|
19
|
-
|
|
30
|
+
|
|
20
31
|
it "should find by name case insensitive" do
|
|
21
32
|
Tag.find_or_create_with_like_by_name("AWESOME").should == @tag
|
|
22
33
|
end
|
|
23
|
-
|
|
34
|
+
|
|
24
35
|
it "should create by name" do
|
|
25
36
|
lambda {
|
|
26
37
|
Tag.find_or_create_with_like_by_name("epic")
|
|
@@ -28,35 +39,77 @@ describe Tag do
|
|
|
28
39
|
end
|
|
29
40
|
end
|
|
30
41
|
|
|
42
|
+
describe "find or create all by any name" do
|
|
43
|
+
before(:each) do
|
|
44
|
+
@tag.name = "awesome"
|
|
45
|
+
@tag.save
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should find by name" do
|
|
49
|
+
Tag.find_or_create_all_with_like_by_name("awesome").should == [@tag]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should find by name case insensitive" do
|
|
53
|
+
Tag.find_or_create_all_with_like_by_name("AWESOME").should == [@tag]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should create by name" do
|
|
57
|
+
lambda {
|
|
58
|
+
Tag.find_or_create_all_with_like_by_name("epic")
|
|
59
|
+
}.should change(Tag, :count).by(1)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should find or create by name" do
|
|
63
|
+
lambda {
|
|
64
|
+
Tag.find_or_create_all_with_like_by_name("awesome", "epic").map(&:name).should == ["awesome", "epic"]
|
|
65
|
+
}.should change(Tag, :count).by(1)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should return an empty array if no tags are specified" do
|
|
69
|
+
Tag.find_or_create_all_with_like_by_name([]).should == []
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
31
73
|
it "should require a name" do
|
|
32
74
|
@tag.valid?
|
|
33
|
-
|
|
75
|
+
|
|
76
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
|
77
|
+
@tag.errors[:name].should == ["can't be blank"]
|
|
78
|
+
else
|
|
79
|
+
@tag.errors[:name].should == "can't be blank"
|
|
80
|
+
end
|
|
81
|
+
|
|
34
82
|
@tag.name = "something"
|
|
35
83
|
@tag.valid?
|
|
36
|
-
|
|
84
|
+
|
|
85
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
|
86
|
+
@tag.errors[:name].should == []
|
|
87
|
+
else
|
|
88
|
+
@tag.errors[:name].should be_nil
|
|
89
|
+
end
|
|
37
90
|
end
|
|
38
|
-
|
|
91
|
+
|
|
39
92
|
it "should equal a tag with the same name" do
|
|
40
93
|
@tag.name = "awesome"
|
|
41
94
|
new_tag = Tag.new(:name => "awesome")
|
|
42
95
|
new_tag.should == @tag
|
|
43
96
|
end
|
|
44
|
-
|
|
97
|
+
|
|
45
98
|
it "should return its name when to_s is called" do
|
|
46
99
|
@tag.name = "cool"
|
|
47
100
|
@tag.to_s.should == "cool"
|
|
48
101
|
end
|
|
49
|
-
|
|
102
|
+
|
|
50
103
|
it "have named_scope named(something)" do
|
|
51
104
|
@tag.name = "cool"
|
|
52
105
|
@tag.save!
|
|
53
106
|
Tag.named('cool').should include(@tag)
|
|
54
107
|
end
|
|
55
|
-
|
|
108
|
+
|
|
56
109
|
it "have named_scope named_like(something)" do
|
|
57
110
|
@tag.name = "cool"
|
|
58
111
|
@tag.save!
|
|
59
112
|
@another_tag = Tag.create!(:name => "coolip")
|
|
60
113
|
Tag.named_like('cool').should include(@tag, @another_tag)
|
|
61
114
|
end
|
|
62
|
-
end
|
|
115
|
+
end
|
|
@@ -2,49 +2,61 @@ require File.dirname(__FILE__) + '/../spec_helper'
|
|
|
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
29
|
@taggable.instance_variable_get("@skill_list").instance_of?(TagList).should be_true
|
|
27
|
-
@taggable.save
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
lambda {
|
|
32
|
+
@taggable.save
|
|
33
|
+
}.should change(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,83 @@ 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
|
-
|
|
92
|
+
|
|
88
93
|
Tag.find(:all).size.should == 1
|
|
89
|
-
TaggableModel.
|
|
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")
|
|
104
|
-
|
|
105
|
-
TaggableModel.all_tag_counts.should_not be_empty
|
|
109
|
+
|
|
110
|
+
TaggableModel.all_tag_counts.all.should_not be_empty
|
|
106
111
|
TaggableModel.all_tag_counts.first.count.should == 3 # ruby
|
|
107
112
|
end
|
|
108
|
-
|
|
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
|
|
123
|
+
|
|
124
|
+
it "should be possible to return writable records" do
|
|
125
|
+
TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
|
126
|
+
TaggableModel.tagged_with("ruby").first(:readonly => false).should_not be_readonly
|
|
127
|
+
end
|
|
128
|
+
end
|
|
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
|
-
|
|
134
|
+
|
|
114
135
|
TaggableModel.tagged_with("ruby").tag_counts.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")
|
|
122
|
-
|
|
143
|
+
|
|
123
144
|
TaggableModel.tagged_with("ruby").all_tag_counts.first.count.should == 3 # ruby
|
|
124
145
|
end
|
|
125
|
-
|
|
146
|
+
|
|
126
147
|
it "should be able to set a custom tag context list" do
|
|
127
148
|
bob = TaggableModel.create(:name => "Bob")
|
|
128
149
|
bob.set_tag_list_on(:rotors, "spinning, jumping")
|
|
@@ -131,89 +152,140 @@ describe "Taggable" do
|
|
|
131
152
|
bob.reload
|
|
132
153
|
bob.tags_on(:rotors).should_not be_empty
|
|
133
154
|
end
|
|
134
|
-
|
|
155
|
+
|
|
135
156
|
it "should be able to find tagged" do
|
|
136
157
|
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
|
|
137
158
|
frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
|
|
138
159
|
steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
|
|
139
|
-
|
|
140
|
-
TaggableModel.
|
|
141
|
-
TaggableModel.
|
|
142
|
-
TaggableModel.
|
|
160
|
+
|
|
161
|
+
TaggableModel.tagged_with("ruby", :order => 'taggable_models.name').to_a.should == [bob, frank, steve]
|
|
162
|
+
TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
|
|
163
|
+
TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
|
|
143
164
|
end
|
|
144
|
-
|
|
165
|
+
|
|
166
|
+
it "should be able to find tagged with any tag" do
|
|
167
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
|
|
168
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
|
|
169
|
+
steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
|
|
170
|
+
|
|
171
|
+
TaggableModel.tagged_with(["ruby", "java"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank, steve]
|
|
172
|
+
TaggableModel.tagged_with(["c++", "fitter"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, steve]
|
|
173
|
+
TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank]
|
|
174
|
+
end
|
|
175
|
+
|
|
145
176
|
it "should be able to find tagged on a custom tag context" do
|
|
146
177
|
bob = TaggableModel.create(:name => "Bob")
|
|
147
178
|
bob.set_tag_list_on(:rotors, "spinning, jumping")
|
|
148
179
|
bob.tag_list_on(:rotors).should == ["spinning","jumping"]
|
|
149
180
|
bob.save
|
|
150
|
-
|
|
181
|
+
|
|
182
|
+
TaggableModel.tagged_with("spinning", :on => :rotors).to_a.should == [bob]
|
|
151
183
|
end
|
|
152
184
|
|
|
153
185
|
it "should be able to use named scopes to chain tag finds" do
|
|
154
186
|
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
|
|
155
187
|
frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
|
|
156
188
|
steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, python')
|
|
157
|
-
|
|
189
|
+
|
|
158
190
|
# 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]
|
|
191
|
+
TaggableModel.tagged_with('rails', :on => :skills, :order => 'taggable_models.name').to_a.should == [bob, frank]
|
|
192
|
+
TaggableModel.tagged_with('happier', :on => :tags, :order => 'taggable_models.name').to_a.should == [bob, steve]
|
|
193
|
+
TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).to_a.should == [bob]
|
|
194
|
+
TaggableModel.tagged_with('rails').tagged_with('happier', :on => :tags).to_a.should == [bob]
|
|
163
195
|
end
|
|
164
|
-
|
|
196
|
+
|
|
165
197
|
it "should be able to find tagged with only the matching tags" do
|
|
166
198
|
bob = TaggableModel.create(:name => "Bob", :tag_list => "lazy, happier")
|
|
167
199
|
frank = TaggableModel.create(:name => "Frank", :tag_list => "fitter, happier, inefficient")
|
|
168
200
|
steve = TaggableModel.create(:name => 'Steve', :tag_list => "fitter, happier")
|
|
169
|
-
|
|
170
|
-
TaggableModel.
|
|
201
|
+
|
|
202
|
+
TaggableModel.tagged_with("fitter, happier", :match_all => true).to_a.should == [steve]
|
|
171
203
|
end
|
|
172
|
-
|
|
204
|
+
|
|
173
205
|
it "should be able to find tagged with some excluded tags" do
|
|
174
206
|
bob = TaggableModel.create(:name => "Bob", :tag_list => "happier, lazy")
|
|
175
207
|
frank = TaggableModel.create(:name => "Frank", :tag_list => "happier")
|
|
176
208
|
steve = TaggableModel.create(:name => 'Steve', :tag_list => "happier")
|
|
209
|
+
|
|
210
|
+
TaggableModel.tagged_with("lazy", :exclude => true).to_a.should == [frank, steve]
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "should not create duplicate taggings" do
|
|
214
|
+
bob = TaggableModel.create(:name => "Bob")
|
|
215
|
+
lambda {
|
|
216
|
+
bob.tag_list << "happier"
|
|
217
|
+
bob.tag_list << "happier"
|
|
218
|
+
bob.save
|
|
219
|
+
}.should change(Tagging, :count).by(1)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
describe "Associations" do
|
|
223
|
+
before(:each) do
|
|
224
|
+
@taggable = TaggableModel.create(:tag_list => "awesome, epic")
|
|
225
|
+
end
|
|
177
226
|
|
|
178
|
-
|
|
227
|
+
it "should not remove tags when creating associated objects" do
|
|
228
|
+
@taggable.untaggable_models.create!
|
|
229
|
+
@taggable.reload
|
|
230
|
+
@taggable.tag_list.should have(2).items
|
|
231
|
+
end
|
|
179
232
|
end
|
|
180
|
-
|
|
233
|
+
|
|
234
|
+
describe "grouped_column_names_for method" do
|
|
235
|
+
it "should return all column names joined for Tag GROUP clause" do
|
|
236
|
+
@taggable.grouped_column_names_for(Tag).should == "tags.id, tags.name"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it "should return all column names joined for TaggableModel GROUP clause" do
|
|
240
|
+
@taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type"
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
181
244
|
describe "Single Table Inheritance" do
|
|
182
245
|
before do
|
|
183
|
-
[TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
|
|
184
246
|
@taggable = TaggableModel.new(:name => "taggable")
|
|
185
247
|
@inherited_same = InheritingTaggableModel.new(:name => "inherited same")
|
|
186
248
|
@inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
|
|
187
249
|
end
|
|
188
|
-
|
|
250
|
+
|
|
189
251
|
it "should be able to save tags for inherited models" do
|
|
190
252
|
@inherited_same.tag_list = "bob, kelso"
|
|
191
253
|
@inherited_same.save
|
|
192
|
-
InheritingTaggableModel.
|
|
254
|
+
InheritingTaggableModel.tagged_with("bob").first.should == @inherited_same
|
|
193
255
|
end
|
|
194
|
-
|
|
256
|
+
|
|
195
257
|
it "should find STI tagged models on the superclass" do
|
|
196
258
|
@inherited_same.tag_list = "bob, kelso"
|
|
197
259
|
@inherited_same.save
|
|
198
|
-
TaggableModel.
|
|
260
|
+
TaggableModel.tagged_with("bob").first.should == @inherited_same
|
|
199
261
|
end
|
|
200
|
-
|
|
262
|
+
|
|
201
263
|
it "should be able to add on contexts only to some subclasses" do
|
|
202
264
|
@inherited_different.part_list = "fork, spoon"
|
|
203
265
|
@inherited_different.save
|
|
204
|
-
InheritingTaggableModel.
|
|
205
|
-
AlteredInheritingTaggableModel.
|
|
266
|
+
InheritingTaggableModel.tagged_with("fork", :on => :parts).should be_empty
|
|
267
|
+
AlteredInheritingTaggableModel.tagged_with("fork", :on => :parts).first.should == @inherited_different
|
|
206
268
|
end
|
|
207
|
-
|
|
269
|
+
|
|
208
270
|
it "should have different tag_counts_on for inherited models" do
|
|
209
271
|
@inherited_same.tag_list = "bob, kelso"
|
|
210
272
|
@inherited_same.save!
|
|
211
273
|
@inherited_different.tag_list = "fork, spoon"
|
|
212
274
|
@inherited_different.save!
|
|
213
|
-
|
|
275
|
+
|
|
214
276
|
InheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso)
|
|
215
277
|
AlteredInheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(fork spoon)
|
|
216
278
|
TaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso fork spoon)
|
|
217
279
|
end
|
|
280
|
+
|
|
281
|
+
it 'should store same tag without validation conflict' do
|
|
282
|
+
@taggable.tag_list = 'one'
|
|
283
|
+
@taggable.save!
|
|
284
|
+
|
|
285
|
+
@inherited_same.tag_list = 'one'
|
|
286
|
+
@inherited_same.save!
|
|
287
|
+
|
|
288
|
+
@inherited_same.update_attributes! :name => 'foo'
|
|
289
|
+
end
|
|
218
290
|
end
|
|
219
291
|
end
|
|
@@ -2,21 +2,89 @@ require File.dirname(__FILE__) + '/../spec_helper'
|
|
|
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(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(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(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(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
|
|
@@ -2,15 +2,30 @@ require File.dirname(__FILE__) + '/../spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe Tagging do
|
|
4
4
|
before(:each) do
|
|
5
|
+
clean_database!
|
|
5
6
|
@tagging = 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
11
|
@tagging.tag = 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 = Tag.create(:name => "awesome")
|
|
26
|
+
|
|
27
|
+
lambda {
|
|
28
|
+
2.times { Tagging.create(:taggable => @taggable, :tag => @tag, :context => 'tags') }
|
|
29
|
+
}.should change(Tagging, :count).by(1)
|
|
15
30
|
end
|
|
16
|
-
end
|
|
31
|
+
end
|
|
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe 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")
|
|
@@ -11,8 +11,6 @@ describe TagsHelper do
|
|
|
11
11
|
@helper = class Helper
|
|
12
12
|
include TagsHelper
|
|
13
13
|
end.new
|
|
14
|
-
|
|
15
|
-
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
it "should yield the proper css classes" do
|