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

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.
@@ -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