ghazel-acts-as-taggable-on 2.0.6.1

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.
Files changed (39) hide show
  1. data/CHANGELOG +25 -0
  2. data/Gemfile +10 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +221 -0
  5. data/Rakefile +59 -0
  6. data/VERSION +1 -0
  7. data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +7 -0
  8. data/generators/acts_as_taggable_on_migration/templates/migration.rb +29 -0
  9. data/lib/acts-as-taggable-on.rb +30 -0
  10. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +53 -0
  11. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +53 -0
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +139 -0
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +262 -0
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +105 -0
  15. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +69 -0
  16. data/lib/acts_as_taggable_on/acts_as_tagger.rb +67 -0
  17. data/lib/acts_as_taggable_on/compatibility/Gemfile +8 -0
  18. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +21 -0
  19. data/lib/acts_as_taggable_on/tag.rb +84 -0
  20. data/lib/acts_as_taggable_on/tag_list.rb +96 -0
  21. data/lib/acts_as_taggable_on/tagging.rb +24 -0
  22. data/lib/acts_as_taggable_on/tags_helper.rb +17 -0
  23. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +32 -0
  24. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +28 -0
  25. data/rails/init.rb +1 -0
  26. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +268 -0
  27. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +114 -0
  28. data/spec/acts_as_taggable_on/tag_list_spec.rb +70 -0
  29. data/spec/acts_as_taggable_on/tag_spec.rb +115 -0
  30. data/spec/acts_as_taggable_on/taggable_spec.rb +333 -0
  31. data/spec/acts_as_taggable_on/tagger_spec.rb +91 -0
  32. data/spec/acts_as_taggable_on/tagging_spec.rb +31 -0
  33. data/spec/acts_as_taggable_on/tags_helper_spec.rb +28 -0
  34. data/spec/bm.rb +52 -0
  35. data/spec/database.yml.sample +17 -0
  36. data/spec/models.rb +31 -0
  37. data/spec/schema.rb +43 -0
  38. data/spec/spec_helper.rb +60 -0
  39. metadata +114 -0
@@ -0,0 +1,24 @@
1
+ module ActsAsTaggableOn
2
+ class Tagging < ::ActiveRecord::Base #:nodoc:
3
+ include ActsAsTaggableOn::ActiveRecord::Backports if ::ActiveRecord::VERSION::MAJOR < 3
4
+
5
+ attr_accessible :tag,
6
+ :tag_id,
7
+ :context,
8
+ :taggable,
9
+ :taggable_type,
10
+ :taggable_id,
11
+ :tagger,
12
+ :tagger_type,
13
+ :tagger_id
14
+
15
+ belongs_to :tag, :class_name => 'ActsAsTaggableOn::Tag'
16
+ belongs_to :taggable, :polymorphic => true
17
+ belongs_to :tagger, :polymorphic => true
18
+
19
+ validates_presence_of :context
20
+ validates_presence_of :tag_id
21
+
22
+ validates_uniqueness_of :tag_id, :scope => [ :taggable_type, :taggable_id, :context, :tagger_id, :tagger_type ]
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ module ActsAsTaggableOn
2
+ module TagsHelper
3
+ # See the README for an example using tag_cloud.
4
+ def tag_cloud(tags, classes)
5
+ tags = tags.all if tags.respond_to?(:all)
6
+
7
+ return [] if tags.empty?
8
+
9
+ max_count = tags.sort_by(&:count).last.count.to_f
10
+
11
+ tags.each do |tag|
12
+ index = ((tag.count / max_count) * (classes.size - 1)).round
13
+ yield tag, classes[index]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module ActsAsTaggableOn
4
+ class MigrationGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+
7
+ desc "Generates migration for Tag and Tagging models"
8
+
9
+ def self.orm
10
+ Rails::Generators.options[:rails][:orm]
11
+ end
12
+
13
+ def self.source_root
14
+ File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)) )
15
+ end
16
+
17
+ def self.orm_has_migration?
18
+ [:active_record].include? orm
19
+ end
20
+
21
+ def self.next_migration_number(path)
22
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
23
+ end
24
+
25
+ def create_migration_file
26
+ if self.class.orm_has_migration?
27
+ migration_template 'migration.rb', 'db/migrate/acts_as_taggable_on_migration'
28
+ end
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,28 @@
1
+ class ActsAsTaggableOnMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tags do |t|
4
+ t.string :name
5
+ end
6
+
7
+ create_table :taggings do |t|
8
+ t.references :tag
9
+
10
+ # You should make sure that the column created is
11
+ # long enough to store the required class names.
12
+ t.references :taggable, :polymorphic => true
13
+ t.references :tagger, :polymorphic => true
14
+
15
+ t.string :context
16
+
17
+ t.datetime :created_at
18
+ end
19
+
20
+ add_index :taggings, :tag_id
21
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
22
+ end
23
+
24
+ def self.down
25
+ drop_table :taggings
26
+ drop_table :tags
27
+ end
28
+ end
@@ -0,0 +1 @@
1
+ require 'acts-as-taggable-on'
@@ -0,0 +1,268 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe "Acts As Taggable On" do
4
+ before(:each) do
5
+ clean_database!
6
+ end
7
+
8
+ it "should provide a class method 'taggable?' that is false for untaggable models" do
9
+ UntaggableModel.should_not be_taggable
10
+ end
11
+
12
+ describe "Taggable Method Generation" do
13
+ before(:each) do
14
+ clean_database!
15
+ TaggableModel.write_inheritable_attribute(:tag_types, [])
16
+ TaggableModel.acts_as_taggable_on(:tags, :languages, :skills, :needs, :offerings)
17
+ @taggable = TaggableModel.new(:name => "Bob Jones")
18
+ end
19
+
20
+ it "should respond 'true' to taggable?" do
21
+ @taggable.class.should be_taggable
22
+ end
23
+
24
+ it "should create a class attribute for tag types" do
25
+ @taggable.class.should respond_to(:tag_types)
26
+ end
27
+
28
+ it "should create an instance attribute for tag types" do
29
+ @taggable.should respond_to(:tag_types)
30
+ end
31
+
32
+ it "should have all tag types" do
33
+ @taggable.tag_types.should == [:tags, :languages, :skills, :needs, :offerings]
34
+ end
35
+
36
+ it "should generate an association for each tag type" do
37
+ @taggable.should respond_to(:tags, :skills, :languages)
38
+ end
39
+
40
+ it "should add tagged_with and tag_counts to singleton" do
41
+ TaggableModel.should respond_to(:tagged_with, :tag_counts)
42
+ end
43
+
44
+ it "should generate a tag_list accessor/setter for each tag type" do
45
+ @taggable.should respond_to(:tag_list, :skill_list, :language_list)
46
+ @taggable.should respond_to(:tag_list=, :skill_list=, :language_list=)
47
+ end
48
+
49
+ it "should generate a tag_list accessor, that includes owned tags, for each tag type" do
50
+ @taggable.should respond_to(:all_tags_list, :all_skills_list, :all_languages_list)
51
+ end
52
+ end
53
+
54
+ describe "Single Table Inheritance" do
55
+ before do
56
+ @taggable = TaggableModel.new(:name => "taggable")
57
+ @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
58
+ @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
59
+ end
60
+
61
+ it "should pass on tag contexts to STI-inherited models" do
62
+ @inherited_same.should respond_to(:tag_list, :skill_list, :language_list)
63
+ @inherited_different.should respond_to(:tag_list, :skill_list, :language_list)
64
+ end
65
+
66
+ it "should have tag contexts added in altered STI models" do
67
+ @inherited_different.should respond_to(:part_list)
68
+ end
69
+ end
70
+
71
+ describe "Reloading" do
72
+ it "should save a model instantiated by Model.find" do
73
+ taggable = TaggableModel.create!(:name => "Taggable")
74
+ found_taggable = TaggableModel.find(taggable.id)
75
+ found_taggable.save
76
+ end
77
+ end
78
+
79
+ describe "Related Objects" do
80
+ it "should find related objects based on tag names on context" do
81
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
82
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
83
+ taggable3 = TaggableModel.create!(:name => "Taggable 3")
84
+
85
+ taggable1.tag_list = "one, two"
86
+ taggable1.save
87
+
88
+ taggable2.tag_list = "three, four"
89
+ taggable2.save
90
+
91
+ taggable3.tag_list = "one, four"
92
+ taggable3.save
93
+
94
+ taggable1.find_related_tags.should include(taggable3)
95
+ taggable1.find_related_tags.should_not include(taggable2)
96
+ end
97
+
98
+ it "should find other related objects based on tag names on context" do
99
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
100
+ taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
101
+ taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
102
+
103
+ taggable1.tag_list = "one, two"
104
+ taggable1.save
105
+
106
+ taggable2.tag_list = "three, four"
107
+ taggable2.save
108
+
109
+ taggable3.tag_list = "one, four"
110
+ taggable3.save
111
+
112
+ taggable1.find_related_tags_for(OtherTaggableModel).should include(taggable3)
113
+ taggable1.find_related_tags_for(OtherTaggableModel).should_not include(taggable2)
114
+ end
115
+
116
+ it "should not include the object itself in the list of related objects" do
117
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
118
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
119
+
120
+ taggable1.tag_list = "one"
121
+ taggable1.save
122
+
123
+ taggable2.tag_list = "one, two"
124
+ taggable2.save
125
+
126
+ taggable1.find_related_tags.should include(taggable2)
127
+ taggable1.find_related_tags.should_not include(taggable1)
128
+ end
129
+ end
130
+
131
+ describe "Matching Contexts" do
132
+ it "should find objects with tags of matching contexts" do
133
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
134
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
135
+ taggable3 = TaggableModel.create!(:name => "Taggable 3")
136
+
137
+ taggable1.offering_list = "one, two"
138
+ taggable1.save!
139
+
140
+ taggable2.need_list = "one, two"
141
+ taggable2.save!
142
+
143
+ taggable3.offering_list = "one, two"
144
+ taggable3.save!
145
+
146
+ taggable1.find_matching_contexts(:offerings, :needs).should include(taggable2)
147
+ taggable1.find_matching_contexts(:offerings, :needs).should_not include(taggable3)
148
+ end
149
+
150
+ it "should find other related objects with tags of matching contexts" do
151
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
152
+ taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
153
+ taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
154
+
155
+ taggable1.offering_list = "one, two"
156
+ taggable1.save
157
+
158
+ taggable2.need_list = "one, two"
159
+ taggable2.save
160
+
161
+ taggable3.offering_list = "one, two"
162
+ taggable3.save
163
+
164
+ taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs).should include(taggable2)
165
+ taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs).should_not include(taggable3)
166
+ end
167
+
168
+ it "should not include the object itself in the list of related objects" do
169
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
170
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
171
+
172
+ taggable1.tag_list = "one"
173
+ taggable1.save
174
+
175
+ taggable2.tag_list = "one, two"
176
+ taggable2.save
177
+
178
+ taggable1.find_related_tags.should include(taggable2)
179
+ taggable1.find_related_tags.should_not include(taggable1)
180
+ end
181
+ end
182
+
183
+ describe 'Tagging Contexts' do
184
+ it 'should eliminate duplicate tagging contexts ' do
185
+ TaggableModel.acts_as_taggable_on(:skills, :skills)
186
+ TaggableModel.tag_types.freq[:skills].should_not == 3
187
+ end
188
+
189
+ it "should not contain embedded/nested arrays" do
190
+ TaggableModel.acts_as_taggable_on([:array], [:array])
191
+ TaggableModel.tag_types.freq[[:array]].should == 0
192
+ end
193
+
194
+ it "should _flatten_ the content of arrays" do
195
+ TaggableModel.acts_as_taggable_on([:array], [:array])
196
+ TaggableModel.tag_types.freq[:array].should == 1
197
+ end
198
+
199
+ it "should not raise an error when passed nil" do
200
+ lambda {
201
+ TaggableModel.acts_as_taggable_on()
202
+ }.should_not raise_error
203
+ end
204
+
205
+ it "should not raise an error when passed [nil]" do
206
+ lambda {
207
+ TaggableModel.acts_as_taggable_on([nil])
208
+ }.should_not raise_error
209
+ end
210
+ end
211
+
212
+ describe 'Caching' do
213
+ before(:each) do
214
+ @taggable = CachedModel.new(:name => "Bob Jones")
215
+ end
216
+
217
+ it "should add saving of tag lists and cached tag lists to the instance" do
218
+ @taggable.should respond_to(:save_cached_tag_list)
219
+ @taggable.should respond_to(:save_tags)
220
+ end
221
+
222
+ it "should generate a cached column checker for each tag type" do
223
+ CachedModel.should respond_to(:caching_tag_list?)
224
+ end
225
+
226
+ it 'should not have cached tags' do
227
+ @taggable.cached_tag_list.should be_blank
228
+ end
229
+
230
+ it 'should cache tags' do
231
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
232
+ @taggable.cached_tag_list.should == 'awesome, epic'
233
+ end
234
+
235
+ it 'should keep the cache' do
236
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
237
+ @taggable = CachedModel.find(@taggable)
238
+ @taggable.save!
239
+ @taggable.cached_tag_list.should == 'awesome, epic'
240
+ end
241
+
242
+ it 'should update the cache' do
243
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
244
+ @taggable.update_attributes(:tag_list => 'awesome')
245
+ @taggable.cached_tag_list.should == 'awesome'
246
+ end
247
+
248
+ it 'should remove the cache' do
249
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
250
+ @taggable.update_attributes(:tag_list => '')
251
+ @taggable.cached_tag_list.should be_blank
252
+ end
253
+
254
+ it 'should have a tag list' do
255
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
256
+ @taggable = CachedModel.find(@taggable.id)
257
+ @taggable.tag_list.sort.should == %w(awesome epic).sort
258
+ end
259
+
260
+ it 'should keep the tag list' do
261
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
262
+ @taggable = CachedModel.find(@taggable.id)
263
+ @taggable.save!
264
+ @taggable.tag_list.sort.should == %w(awesome epic).sort
265
+ end
266
+ end
267
+
268
+ end
@@ -0,0 +1,114 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe "acts_as_tagger" do
4
+ before(:each) do
5
+ clean_database!
6
+ end
7
+
8
+ describe "Tagger Method Generation" do
9
+ before(:each) do
10
+ @tagger = TaggableUser.new()
11
+ end
12
+
13
+ it "should add #is_tagger? query method to the class-side" do
14
+ TaggableUser.should respond_to(:is_tagger?)
15
+ end
16
+
17
+ it "should return true from the class-side #is_tagger?" do
18
+ TaggableUser.is_tagger?.should be_true
19
+ end
20
+
21
+ it "should return false from the base #is_tagger?" do
22
+ ActiveRecord::Base.is_tagger?.should be_false
23
+ end
24
+
25
+ it "should add #is_tagger? query method to the singleton" do
26
+ @tagger.should respond_to(:is_tagger?)
27
+ end
28
+
29
+ it "should add #tag method on the instance-side" do
30
+ @tagger.should respond_to(:tag)
31
+ end
32
+
33
+ it "should generate an association for #owned_taggings and #owned_tags" do
34
+ @tagger.should respond_to(:owned_taggings, :owned_tags)
35
+ end
36
+ end
37
+
38
+ describe "#tag" do
39
+ context 'when called with a non-existent tag context' do
40
+ before(:each) do
41
+ @tagger = TaggableUser.new()
42
+ @taggable = TaggableModel.new(:name=>"Richard Prior")
43
+ end
44
+
45
+ it "should by default not throw an exception " do
46
+ @taggable.tag_list_on(:foo).should be_empty
47
+ lambda {
48
+ @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo)
49
+ }.should_not raise_error
50
+ end
51
+
52
+ it 'should by default create the tag context on-the-fly' do
53
+ @taggable.tag_list_on(:here_ond_now).should be_empty
54
+ @tagger.tag(@taggable, :with=>'that', :on => :here_ond_now)
55
+ @taggable.tag_list_on(:here_ond_now).should_not include('that')
56
+ @taggable.all_tags_list_on(:here_ond_now).should include('that')
57
+ end
58
+
59
+ it "should show all the tag list when both public and owned tags exist" do
60
+ @taggable.tag_list = 'ruby, python'
61
+ @tagger.tag(@taggable, :with => 'java, lisp', :on => :tags)
62
+ @taggable.all_tags_on(:tags).map(&:name).sort.should == %w(ruby python java lisp).sort
63
+ end
64
+
65
+ it "should not add owned tags to the common list" do
66
+ @taggable.tag_list = 'ruby, python'
67
+ @tagger.tag(@taggable, :with => 'java, lisp', :on => :tags)
68
+ @taggable.tag_list.should == %w(ruby python)
69
+ @tagger.tag(@taggable, :with => '', :on => :tags)
70
+ @taggable.tag_list.should == %w(ruby python)
71
+ end
72
+
73
+ it "should throw an exception when the default is over-ridden" do
74
+ @taggable.tag_list_on(:foo_boo).should be_empty
75
+ lambda {
76
+ @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo_boo, :force=>false)
77
+ }.should raise_error
78
+ end
79
+
80
+ it "should not create the tag context on-the-fly when the default is over-ridden" do
81
+ @taggable.tag_list_on(:foo_boo).should be_empty
82
+ @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo_boo, :force=>false) rescue
83
+ @taggable.tag_list_on(:foo_boo).should be_empty
84
+ end
85
+ end
86
+
87
+ describe "when called by multiple tagger's" do
88
+ before(:each) do
89
+ @user_x = TaggableUser.create(:name => "User X")
90
+ @user_y = TaggableUser.create(:name => "User Y")
91
+ @taggable = TaggableModel.create(:name => 'acts_as_taggable_on', :tag_list => 'plugin')
92
+
93
+ @user_x.tag(@taggable, :with => 'ruby, rails', :on => :tags)
94
+ @user_y.tag(@taggable, :with => 'ruby, plugin', :on => :tags)
95
+
96
+ @user_y.tag(@taggable, :with => '', :on => :tags)
97
+ @user_y.tag(@taggable, :with => '', :on => :tags)
98
+ end
99
+
100
+ it "should delete owned tags" do
101
+ @user_y.owned_tags.should == []
102
+ end
103
+
104
+ it "should not delete other taggers tags" do
105
+ @user_x.owned_tags.should have(2).items
106
+ end
107
+
108
+ it "should not delete original tags" do
109
+ @taggable.all_tags_list_on(:tags).should include('plugin')
110
+ end
111
+ end
112
+ end
113
+
114
+ end