acts-as-taggable-on 2.0.0.pre1 → 2.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,60 +1,50 @@
1
- module ActiveRecord
2
- module Acts
3
- module Tagger
4
-
5
- def self.included(base)
6
- base.extend ClassMethods
7
- end
8
-
9
- module ClassMethods
10
-
11
- def acts_as_tagger(opts={})
12
- has_many :owned_taggings, opts.merge(:as => :tagger, :dependent => :destroy,
13
- :include => :tag, :class_name => "Tagging")
14
- has_many :owned_tags, :through => :owned_taggings, :source => :tag, :uniq => true
15
-
16
- include ActiveRecord::Acts::Tagger::InstanceMethods
17
- extend ActiveRecord::Acts::Tagger::SingletonMethods
18
- end
1
+ module ActsAsTaggableOn
2
+ module Tagger
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
19
6
 
20
- def is_tagger?
21
- false
22
- end
7
+ module ClassMethods
8
+ def acts_as_tagger(opts={})
9
+ has_many :owned_taggings, opts.merge(:as => :tagger, :dependent => :destroy,
10
+ :include => :tag, :class_name => "Tagging")
11
+ has_many :owned_tags, :through => :owned_taggings, :source => :tag, :uniq => true
23
12
 
13
+ include ActsAsTaggableOn::Tagger::InstanceMethods
14
+ extend ActsAsTaggableOn::Tagger::SingletonMethods
24
15
  end
25
16
 
26
- module InstanceMethods
27
-
28
- def self.included(base)
29
- end
30
-
31
- def tag(taggable, opts={})
32
- opts.reverse_merge!(:force => true)
17
+ def is_tagger?
18
+ false
19
+ end
20
+ end
33
21
 
34
- return false unless taggable.respond_to?(:is_taggable?) && taggable.is_taggable?
22
+ module InstanceMethods
23
+ def self.included(base)
24
+ end
35
25
 
36
- raise "You need to specify a tag context using :on" unless opts.has_key?(:on)
37
- raise "You need to specify some tags using :with" unless opts.has_key?(:with)
38
- raise "No context :#{opts[:on]} defined in #{taggable.class.to_s}" unless (opts[:force] || taggable.tag_types.include?(opts[:on]))
26
+ def tag(taggable, opts={})
27
+ opts.reverse_merge!(:force => true)
39
28
 
40
- taggable.set_tag_list_on(opts[:on].to_s, opts[:with], self)
41
- taggable.save
42
- end
29
+ return false unless taggable.respond_to?(:is_taggable?) && taggable.is_taggable?
43
30
 
44
- def is_tagger?
45
- self.class.is_tagger?
46
- end
31
+ raise "You need to specify a tag context using :on" unless opts.has_key?(:on)
32
+ raise "You need to specify some tags using :with" unless opts.has_key?(:with)
33
+ raise "No context :#{opts[:on]} defined in #{taggable.class.to_s}" unless (opts[:force] || taggable.tag_types.include?(opts[:on]))
47
34
 
35
+ taggable.set_owner_tag_list_on(self, opts[:on].to_s, opts[:with])
36
+ taggable.save
48
37
  end
49
38
 
50
- module SingletonMethods
51
-
52
- def is_tagger?
53
- true
54
- end
55
-
39
+ def is_tagger?
40
+ self.class.is_tagger?
56
41
  end
42
+ end
57
43
 
44
+ module SingletonMethods
45
+ def is_tagger?
46
+ true
47
+ end
58
48
  end
59
49
  end
60
50
  end
@@ -0,0 +1,6 @@
1
+ source :gemcutter
2
+
3
+ # Rails 2.3
4
+ gem 'rails', '2.3.5'
5
+ gem 'rspec', '1.3.0', :require => 'spec'
6
+ gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
@@ -0,0 +1,17 @@
1
+ module ActsAsTaggableOn
2
+ module ActiveRecord
3
+ module Backports
4
+ def self.included(base)
5
+ base.class_eval do
6
+ named_scope :where, lambda { |conditions| { :conditions => conditions } }
7
+ named_scope :joins, lambda { |joins| { :joins => joins } }
8
+ named_scope :group, lambda { |group| { :group => group } }
9
+ named_scope :readonly, lambda { |readonly| { :readonly => readonly } }
10
+ named_scope :order, lambda { |order| { :order => order } }
11
+ named_scope :select, lambda { |select| { :select => select } }
12
+ named_scope :limit, lambda { |limit| { :limit => limit } }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ class Tag < ActiveRecord::Base
2
+ include ActsAsTaggableOn::ActiveRecord::Backports
3
+ end
@@ -0,0 +1,3 @@
1
+ class Tagging < ActiveRecord::Base
2
+ include ActsAsTaggableOn::ActiveRecord::Backports
3
+ end
@@ -11,12 +11,23 @@ class Tag < ActiveRecord::Base
11
11
  validates_presence_of :name
12
12
  validates_uniqueness_of :name
13
13
 
14
- ### NAMED SCOPES:
14
+ ### SCOPES:
15
15
 
16
- scope :named, lambda { |name| { :conditions => ["name LIKE ?", name] } }
17
- scope :named_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR ") } }
18
- scope :named_like, lambda { |name| { :conditions => ["name LIKE ?", "%#{name}%"] } }
19
- scope :named_like_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", "%#{tag.to_s}%"]) }.join(" OR ") } }
16
+ def self.named(name)
17
+ where(["name LIKE ?", name])
18
+ end
19
+
20
+ def self.named_any(list)
21
+ where(list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR "))
22
+ end
23
+
24
+ def self.named_like(name)
25
+ where(["name LIKE ?", "%#{name}%"])
26
+ end
27
+
28
+ def self.named_like_any(list)
29
+ where(list.map { |tag| sanitize_sql(["name LIKE ?", "%#{tag.to_s}%"]) }.join(" OR "))
30
+ end
20
31
 
21
32
  ### CLASS METHODS:
22
33
 
@@ -2,7 +2,7 @@ module TagsHelper
2
2
 
3
3
  # See the README for an example using tag_cloud.
4
4
  def tag_cloud(tags, classes)
5
- tags = tags.all if tags.is_a? ActiveRecord::Relation
5
+ tags = tags.all if tags.respond_to?(:all)
6
6
 
7
7
  return [] if tags.empty?
8
8
 
@@ -26,6 +26,10 @@ describe "Acts As Taggable On" do
26
26
  it "should create an instance attribute for tag types" do
27
27
  @taggable.should respond_to(:tag_types)
28
28
  end
29
+
30
+ it "should have all tag types" do
31
+ @taggable.tag_types.should == [:tags, :languages, :skills, :needs, :offerings]
32
+ end
29
33
 
30
34
  it "should generate an association for each tag type" do
31
35
  @taggable.should respond_to(:tags, :skills, :languages)
@@ -48,6 +52,10 @@ describe "Acts As Taggable On" do
48
52
  @taggable.should respond_to(:tag_list, :skill_list, :language_list)
49
53
  @taggable.should respond_to(:tag_list=, :skill_list=, :language_list=)
50
54
  end
55
+
56
+ it "should generate a tag_list accessor, that includes owned tags, for each tag type" do
57
+ @taggable.should respond_to(:all_tags_list, :all_skills_list, :all_languages_list)
58
+ end
51
59
  end
52
60
 
53
61
  describe "Single Table Inheritance" do
@@ -56,17 +64,17 @@ describe "Acts As Taggable On" do
56
64
  @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
57
65
  @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
58
66
  end
59
-
67
+
60
68
  it "should pass on tag contexts to STI-inherited models" do
61
69
  @inherited_same.should respond_to(:tag_list, :skill_list, :language_list)
62
70
  @inherited_different.should respond_to(:tag_list, :skill_list, :language_list)
63
71
  end
64
-
72
+
65
73
  it "should have tag contexts added in altered STI models" do
66
74
  @inherited_different.should respond_to(:part_list)
67
75
  end
68
76
  end
69
-
77
+
70
78
  describe "Reloading" do
71
79
  it "should save a model instantiated by Model.find" do
72
80
  taggable = TaggableModel.create!(:name => "Taggable")
@@ -80,48 +88,48 @@ describe "Acts As Taggable On" do
80
88
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
81
89
  taggable2 = TaggableModel.create!(:name => "Taggable 2")
82
90
  taggable3 = TaggableModel.create!(:name => "Taggable 3")
83
-
91
+
84
92
  taggable1.tag_list = "one, two"
85
93
  taggable1.save
86
-
94
+
87
95
  taggable2.tag_list = "three, four"
88
96
  taggable2.save
89
-
97
+
90
98
  taggable3.tag_list = "one, four"
91
99
  taggable3.save
92
-
100
+
93
101
  taggable1.find_related_tags.should include(taggable3)
94
102
  taggable1.find_related_tags.should_not include(taggable2)
95
103
  end
96
-
104
+
97
105
  it "should find other related objects based on tag names on context" do
98
106
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
99
107
  taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
100
108
  taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
101
-
109
+
102
110
  taggable1.tag_list = "one, two"
103
111
  taggable1.save
104
-
112
+
105
113
  taggable2.tag_list = "three, four"
106
114
  taggable2.save
107
-
115
+
108
116
  taggable3.tag_list = "one, four"
109
117
  taggable3.save
110
-
118
+
111
119
  taggable1.find_related_tags_for(OtherTaggableModel).should include(taggable3)
112
120
  taggable1.find_related_tags_for(OtherTaggableModel).should_not include(taggable2)
113
121
  end
114
-
122
+
115
123
  it "should not include the object itself in the list of related objects" do
116
124
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
117
125
  taggable2 = TaggableModel.create!(:name => "Taggable 2")
118
-
126
+
119
127
  taggable1.tag_list = "one"
120
128
  taggable1.save
121
-
129
+
122
130
  taggable2.tag_list = "one, two"
123
131
  taggable2.save
124
-
132
+
125
133
  taggable1.find_related_tags.should include(taggable2)
126
134
  taggable1.find_related_tags.should_not include(taggable1)
127
135
  end
@@ -132,48 +140,48 @@ describe "Acts As Taggable On" do
132
140
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
133
141
  taggable2 = TaggableModel.create!(:name => "Taggable 2")
134
142
  taggable3 = TaggableModel.create!(:name => "Taggable 3")
135
-
143
+
136
144
  taggable1.offering_list = "one, two"
137
145
  taggable1.save!
138
-
146
+
139
147
  taggable2.need_list = "one, two"
140
148
  taggable2.save!
141
-
149
+
142
150
  taggable3.offering_list = "one, two"
143
151
  taggable3.save!
144
-
152
+
145
153
  taggable1.find_matching_contexts(:offerings, :needs).should include(taggable2)
146
154
  taggable1.find_matching_contexts(:offerings, :needs).should_not include(taggable3)
147
155
  end
148
-
156
+
149
157
  it "should find other related objects with tags of matching contexts" do
150
158
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
151
159
  taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
152
160
  taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
153
-
161
+
154
162
  taggable1.offering_list = "one, two"
155
163
  taggable1.save
156
-
164
+
157
165
  taggable2.need_list = "one, two"
158
166
  taggable2.save
159
-
167
+
160
168
  taggable3.offering_list = "one, two"
161
169
  taggable3.save
162
-
170
+
163
171
  taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs).should include(taggable2)
164
172
  taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs).should_not include(taggable3)
165
173
  end
166
-
174
+
167
175
  it "should not include the object itself in the list of related objects" do
168
176
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
169
177
  taggable2 = TaggableModel.create!(:name => "Taggable 2")
170
-
178
+
171
179
  taggable1.tag_list = "one"
172
180
  taggable1.save
173
-
181
+
174
182
  taggable2.tag_list = "one, two"
175
183
  taggable2.save
176
-
184
+
177
185
  taggable1.find_related_tags.should include(taggable2)
178
186
  taggable1.find_related_tags.should_not include(taggable1)
179
187
  end
@@ -72,10 +72,21 @@ describe Tag do
72
72
 
73
73
  it "should require a name" do
74
74
  @tag.valid?
75
- @tag.errors[:name].should == ["can't be blank"]
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
+
76
82
  @tag.name = "something"
77
83
  @tag.valid?
78
- @tag.errors[:name].should == []
84
+
85
+ if ActiveRecord::VERSION::MAJOR >= 3
86
+ @tag.errors[:name].should == []
87
+ else
88
+ @tag.errors[:name].should be_nil
89
+ end
79
90
  end
80
91
 
81
92
  it "should equal a tag with the same name" do
@@ -7,10 +7,11 @@ describe "Taggable" do
7
7
  end
8
8
 
9
9
  it "should have tag types" do
10
- for type in [:tags, :languages, :skills, :needs, :offerings]
11
- TaggableModel.tag_types.should include type
12
- end
13
- @taggable.tag_types.should == TaggableModel.tag_types
10
+ [:tags, :languages, :skills, :needs, :offerings].each do |type|
11
+ TaggableModel.tag_types.should include type
12
+ end
13
+
14
+ @taggable.tag_types.should == TaggableModel.tag_types
14
15
  end
15
16
 
16
17
  it "should have tag_counts_on" do
@@ -25,18 +26,21 @@ describe "Taggable" do
25
26
 
26
27
  it "should be able to create tags" do
27
28
  @taggable.skill_list = "ruby, rails, css"
28
- @taggable.instance_variable_get("@skill_list").instance_of?(Hash).should be_true
29
- @taggable.instance_variable_get("@skill_list")[nil].instance_of?(TagList).should be_true
30
- @taggable.save
31
-
32
- Tag.find(:all).size.should == 3
29
+ @taggable.instance_variable_get("@skill_list").instance_of?(TagList).should be_true
30
+
31
+ lambda {
32
+ @taggable.save
33
+ }.should change(Tag, :count).by(3)
33
34
  end
34
35
 
35
36
  it "should be able to create tags through the tag list directly" do
36
37
  @taggable.tag_list_on(:test).add("hello")
37
38
  @taggable.tag_list_cache_on(:test).should_not be_empty
39
+ @taggable.tag_list_on(:test).should == ["hello"]
40
+
38
41
  @taggable.save
39
42
  @taggable.save_tags
43
+
40
44
  @taggable.reload
41
45
  @taggable.tag_list_on(:test).should == ["hello"]
42
46
  end
@@ -171,10 +175,10 @@ describe "Taggable" do
171
175
  steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, python')
172
176
 
173
177
  # Let's only find those productive Rails developers
174
- TaggableModel.tagged_with('rails', :on => :skills, :order => 'taggable_models.name').should == [bob, frank]
175
- TaggableModel.tagged_with('happier', :on => :tags, :order => 'taggable_models.name').should == [bob, steve]
176
- TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).should == [bob]
177
- TaggableModel.tagged_with('rails').tagged_with('happier', :on => :tags).should == [bob]
178
+ TaggableModel.tagged_with('rails', :on => :skills, :order => 'taggable_models.name').all.should == [bob, frank]
179
+ TaggableModel.tagged_with('happier', :on => :tags, :order => 'taggable_models.name').all.should == [bob, steve]
180
+ TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).all.should == [bob]
181
+ TaggableModel.tagged_with('rails').tagged_with('happier', :on => :tags).all.should == [bob]
178
182
  end
179
183
 
180
184
  it "should be able to find tagged with only the matching tags" do
@@ -202,6 +206,16 @@ describe "Taggable" do
202
206
  }.should change(Tagging, :count).by(1)
203
207
  end
204
208
 
209
+ describe "grouped_column_names_for method" do
210
+ it "should return all column names joined for Tag GROUP clause" do
211
+ @taggable.grouped_column_names_for(Tag).should == "tags.id, tags.name"
212
+ end
213
+
214
+ it "should return all column names joined for TaggableModel GROUP clause" do
215
+ @taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type, taggable_models.cached_tag_list"
216
+ end
217
+ end
218
+
205
219
  describe "Single Table Inheritance" do
206
220
  before do
207
221
  [TaggableModel, Tag, Tagging, TaggableUser].each(&:delete_all)
@@ -209,44 +223,44 @@ describe "Taggable" do
209
223
  @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
210
224
  @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
211
225
  end
212
-
226
+
213
227
  it "should be able to save tags for inherited models" do
214
228
  @inherited_same.tag_list = "bob, kelso"
215
229
  @inherited_same.save
216
230
  InheritingTaggableModel.tagged_with("bob").first.should == @inherited_same
217
231
  end
218
-
232
+
219
233
  it "should find STI tagged models on the superclass" do
220
234
  @inherited_same.tag_list = "bob, kelso"
221
235
  @inherited_same.save
222
236
  TaggableModel.tagged_with("bob").first.should == @inherited_same
223
237
  end
224
-
238
+
225
239
  it "should be able to add on contexts only to some subclasses" do
226
240
  @inherited_different.part_list = "fork, spoon"
227
241
  @inherited_different.save
228
242
  InheritingTaggableModel.tagged_with("fork", :on => :parts).should be_empty
229
243
  AlteredInheritingTaggableModel.tagged_with("fork", :on => :parts).first.should == @inherited_different
230
244
  end
231
-
245
+
232
246
  it "should have different tag_counts_on for inherited models" do
233
247
  @inherited_same.tag_list = "bob, kelso"
234
248
  @inherited_same.save!
235
249
  @inherited_different.tag_list = "fork, spoon"
236
250
  @inherited_different.save!
237
-
251
+
238
252
  InheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso)
239
253
  AlteredInheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(fork spoon)
240
254
  TaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso fork spoon)
241
255
  end
242
-
256
+
243
257
  it 'should store same tag without validation conflict' do
244
258
  @taggable.tag_list = 'one'
245
259
  @taggable.save!
246
-
260
+
247
261
  @inherited_same.tag_list = 'one'
248
262
  @inherited_same.save!
249
-
263
+
250
264
  @inherited_same.update_attributes! :name => 'foo'
251
265
  end
252
266
  end