tagtical 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/CHANGELOG +25 -0
  2. data/Gemfile +20 -0
  3. data/Gemfile.lock +25 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.rdoc +306 -0
  6. data/Rakefile +59 -0
  7. data/VERSION +1 -0
  8. data/generators/tagtical_migration/tagtical_migration_generator.rb +7 -0
  9. data/generators/tagtical_migration/templates/migration.rb +34 -0
  10. data/lib/generators/tagtical/migration/migration_generator.rb +32 -0
  11. data/lib/generators/tagtical/migration/templates/active_record/migration.rb +35 -0
  12. data/lib/tagtical/acts_as_tagger.rb +69 -0
  13. data/lib/tagtical/compatibility/Gemfile +8 -0
  14. data/lib/tagtical/compatibility/active_record_backports.rb +21 -0
  15. data/lib/tagtical/tag.rb +314 -0
  16. data/lib/tagtical/tag_list.rb +133 -0
  17. data/lib/tagtical/taggable/cache.rb +53 -0
  18. data/lib/tagtical/taggable/collection.rb +141 -0
  19. data/lib/tagtical/taggable/core.rb +317 -0
  20. data/lib/tagtical/taggable/ownership.rb +110 -0
  21. data/lib/tagtical/taggable/related.rb +60 -0
  22. data/lib/tagtical/taggable.rb +51 -0
  23. data/lib/tagtical/tagging.rb +42 -0
  24. data/lib/tagtical/tags_helper.rb +17 -0
  25. data/lib/tagtical.rb +47 -0
  26. data/rails/init.rb +1 -0
  27. data/spec/bm.rb +53 -0
  28. data/spec/database.yml +17 -0
  29. data/spec/database.yml.sample +17 -0
  30. data/spec/models.rb +60 -0
  31. data/spec/schema.rb +46 -0
  32. data/spec/spec_helper.rb +159 -0
  33. data/spec/tagtical/acts_as_tagger_spec.rb +94 -0
  34. data/spec/tagtical/tag_list_spec.rb +102 -0
  35. data/spec/tagtical/tag_spec.rb +301 -0
  36. data/spec/tagtical/taggable_spec.rb +460 -0
  37. data/spec/tagtical/tagger_spec.rb +76 -0
  38. data/spec/tagtical/tagging_spec.rb +52 -0
  39. data/spec/tagtical/tags_helper_spec.rb +28 -0
  40. data/spec/tagtical/tagtical_spec.rb +340 -0
  41. metadata +132 -0
@@ -0,0 +1,340 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe Tagtical 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(: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 methods for each tag_type" do
45
+ TaggableModel.should respond_to(:with_tags, :with_languages, :with_skills, :with_needs, :with_offerings)
46
+ end
47
+
48
+ it "should generate a tag_list accessor/setter for each tag type" do
49
+ @taggable.should respond_to(:tag_list, :skill_list, :language_list)
50
+ @taggable.should respond_to(:tag_list=, :skill_list=, :language_list=)
51
+ end
52
+
53
+ it "should generate a tag_list accessor, that includes owned tags, for each tag type" do
54
+ @taggable.should respond_to(:all_tags_list, :all_skills_list, :all_languages_list)
55
+ end
56
+ end
57
+
58
+ describe "Single Table Inheritance" do
59
+ before do
60
+ @taggable = TaggableModel.new(:name => "taggable")
61
+ @inherited_same = InheritingTaggableModel.new(:name => "inherited same")
62
+ @inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
63
+ end
64
+
65
+ it "should pass on tag contexts to STI-inherited models" do
66
+ @inherited_same.should respond_to(:tag_list, :skill_list, :language_list)
67
+ @inherited_different.should respond_to(:tag_list, :skill_list, :language_list)
68
+ end
69
+
70
+ it "should have tag contexts added in altered STI models" do
71
+ @inherited_different.should respond_to(:part_list)
72
+ end
73
+ end
74
+
75
+ describe "Reloading" do
76
+ it "should save a model instantiated by Model.find" do
77
+ taggable = TaggableModel.create!(:name => "Taggable")
78
+ found_taggable = TaggableModel.find(taggable.id)
79
+ found_taggable.save
80
+ end
81
+ end
82
+
83
+ describe "Related Objects" do
84
+ it "should find related objects based on tag names on context" do
85
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
86
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
87
+ taggable3 = TaggableModel.create!(:name => "Taggable 3")
88
+
89
+ taggable1.tag_list = "one, two"
90
+ taggable1.save
91
+
92
+ taggable2.tag_list = "three, four"
93
+ taggable2.save
94
+
95
+ taggable3.tag_list = "one, four"
96
+ taggable3.save
97
+
98
+ taggable1.find_related_tags.should include(taggable3)
99
+ taggable1.find_related_tags.should_not include(taggable2)
100
+ end
101
+
102
+ it "should find other related objects based on tag names on context" do
103
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
104
+ taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
105
+ taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
106
+
107
+ taggable1.tag_list = "one, two"
108
+ taggable1.save
109
+
110
+ taggable2.tag_list = "three, four"
111
+ taggable2.save
112
+
113
+ taggable3.tag_list = "one, four"
114
+ taggable3.save
115
+
116
+ taggable1.find_related_tags_for(OtherTaggableModel).should include(taggable3)
117
+ taggable1.find_related_tags_for(OtherTaggableModel).should_not include(taggable2)
118
+ end
119
+
120
+ it "should not include the object itself in the list of related objects" do
121
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
122
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
123
+
124
+ taggable1.tag_list = "one"
125
+ taggable1.save
126
+
127
+ taggable2.tag_list = "one, two"
128
+ taggable2.save
129
+
130
+ taggable1.find_related_tags.should include(taggable2)
131
+ taggable1.find_related_tags.should_not include(taggable1)
132
+ end
133
+ end
134
+
135
+ describe "Matching Contexts" do
136
+ it "should find objects with tags of matching contexts" do
137
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
138
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
139
+ taggable3 = TaggableModel.create!(:name => "Taggable 3")
140
+
141
+ taggable1.offering_list = "one, two"
142
+ taggable1.save!
143
+
144
+ taggable2.need_list = "one, two"
145
+ taggable2.save!
146
+
147
+ taggable3.offering_list = "one, two"
148
+ taggable3.save!
149
+
150
+ taggable1.find_matching_contexts(:offerings, :needs).should include(taggable2)
151
+ taggable1.find_matching_contexts(:offerings, :needs).should_not include(taggable3)
152
+ end
153
+
154
+ it "should find other related objects with tags of matching contexts" do
155
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
156
+ taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")
157
+ taggable3 = OtherTaggableModel.create!(:name => "Taggable 3")
158
+
159
+ taggable1.offering_list = "one, two"
160
+ taggable1.save
161
+
162
+ taggable2.need_list = "one, two"
163
+ taggable2.save
164
+
165
+ taggable3.offering_list = "one, two"
166
+ taggable3.save
167
+
168
+ taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs).should include(taggable2)
169
+ taggable1.find_matching_contexts_for(OtherTaggableModel, :offerings, :needs).should_not include(taggable3)
170
+ end
171
+
172
+ it "should not include the object itself in the list of related objects" do
173
+ taggable1 = TaggableModel.create!(:name => "Taggable 1")
174
+ taggable2 = TaggableModel.create!(:name => "Taggable 2")
175
+
176
+ taggable1.tag_list = "one"
177
+ taggable1.save
178
+
179
+ taggable2.tag_list = "one, two"
180
+ taggable2.save
181
+
182
+ taggable1.find_related_tags.should include(taggable2)
183
+ taggable1.find_related_tags.should_not include(taggable1)
184
+ end
185
+ end
186
+
187
+ describe 'Tagging Contexts' do
188
+ it 'should eliminate duplicate tagging contexts ' do
189
+ TaggableModel.acts_as_taggable(:skills, :skills)
190
+ TaggableModel.tag_types.freq["skill"].should == 1
191
+ end
192
+
193
+ it "should not contain embedded/nested arrays" do
194
+ TaggableModel.acts_as_taggable([:skills], [:skills])
195
+ TaggableModel.tag_types.freq[[:skills]].should == 0
196
+ end
197
+
198
+ it "should _flatten_ the content of arrays" do
199
+ TaggableModel.acts_as_taggable([:skills], [:skills])
200
+ TaggableModel.tag_types.freq["skill"].should == 1
201
+ end
202
+
203
+ it "should support tag_types without an associated Tag subclass" do
204
+ lambda { TaggableModel.acts_as_taggable(:doesnt_exist) }.should_not raise_error
205
+ end
206
+
207
+ it "should not raise an error when passed nil" do
208
+ lambda {
209
+ TaggableModel.acts_as_taggable()
210
+ }.should_not raise_error
211
+ end
212
+
213
+ it "should not raise an error when passed [nil]" do
214
+ lambda {
215
+ TaggableModel.acts_as_taggable([nil])
216
+ }.should_not raise_error
217
+ end
218
+
219
+ context "when Tag subclass is not present" do
220
+
221
+ before do
222
+ TaggableModel.acts_as_taggable(:machines)
223
+ @taggable_model = TaggableModel.create!(:name => "Taggable 1", :tag_list => "random, tags")
224
+ @taggable_model.machine_list = "car, plane, train"
225
+ @taggable_model.save!
226
+ @taggable_model.reload
227
+ @tag_type = @taggable_model.tag_types.find { |t| t=="machine" }
228
+ end
229
+
230
+ specify { @tag_type.klass.should be_nil }
231
+
232
+ it "should have basic tagging methods" do
233
+ @taggable_model.machines.map(&:value).sort.should == ["car", "plane", "train"]
234
+ @taggable_model.tags.machines.map(&:value).sort.should == ["car", "plane", "train"]
235
+ end
236
+
237
+ it "should instantiate with Tagtical::Tag" do
238
+ @taggable_model.machines.each { |tag| tag.should be_an_instance_of Tagtical::Tag }
239
+ end
240
+
241
+ end
242
+ end
243
+
244
+ describe "Tagging with Symbols" do
245
+ it "should tag with symbols" do
246
+ @taggable_model = TaggableModel.create!(:name => "Taggable", :tag_list => [:tag1, :tag2])
247
+ @taggable_model.tag_list.should == ["tag1", "tag2"]
248
+ end
249
+
250
+ end
251
+
252
+ describe "Tagging With Relevance" do
253
+ before do
254
+ @taggable_model = TaggableModel.create!(:name => "Taggable", :tag_list => {"random" => 0.45, "tags" => 1.54}, :skill_list => "tennis, pottery")
255
+ end
256
+
257
+ it "should have tags with relevance" do
258
+ @taggable_model.tags.sort.map(&:relevance).should == [0.45, Tagtical::Tagging.default_relevance, Tagtical::Tagging.default_relevance, 1.54]
259
+ end
260
+
261
+ it "should have skills with relevance" do
262
+ @taggable_model.skills.first.relevance.should == Tagtical::Tagging.default_relevance
263
+ end
264
+
265
+ it "should overwrite relevance" do
266
+ @taggable_model.tag_list = {"tennis" => 6.0}
267
+ @taggable_model.save!
268
+ @taggable_model.reload
269
+ @taggable_model.skills.find_by_value("tennis").relevance.should == 6.0
270
+ end
271
+
272
+ it "should be able to change the tag type and update the relevance" do
273
+ @taggable_model.craft_list = {"pottery" => 4.56}
274
+ @taggable_model.save!
275
+ @taggable_model.reload
276
+ skills = @taggable_model.skills.find_all_by_value("pottery")
277
+ skills.should have(1).items
278
+ skills.first.relevance.should == 4.56
279
+ skills.first.should be_an_instance_of Tag::Craft
280
+ end
281
+
282
+ end
283
+
284
+ describe 'Caching' do
285
+ before(:each) do
286
+ @taggable = CachedModel.new(:name => "Bob Jones")
287
+ end
288
+
289
+ it "should add saving of tag lists and cached tag lists to the instance" do
290
+ @taggable.should respond_to(:save_cached_tag_list)
291
+ @taggable.should respond_to(:save_tags)
292
+ end
293
+
294
+ it "should generate a cached column checker for each tag type" do
295
+ CachedModel.should respond_to(:caching_tag_list?)
296
+ end
297
+
298
+ it 'should not have cached tags' do
299
+ @taggable.cached_tag_list.should be_blank
300
+ end
301
+
302
+ it 'should cache tags' do
303
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
304
+ @taggable.cached_tag_list.should == 'awesome, epic'
305
+ end
306
+
307
+ it 'should keep the cache' do
308
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
309
+ @taggable = CachedModel.find(@taggable)
310
+ @taggable.save!
311
+ @taggable.cached_tag_list.should == 'awesome, epic'
312
+ end
313
+
314
+ it 'should update the cache' do
315
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
316
+ @taggable.update_attributes(:tag_list => 'awesome')
317
+ @taggable.cached_tag_list.should == 'awesome'
318
+ end
319
+
320
+ it 'should remove the cache' do
321
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
322
+ @taggable.update_attributes(:tag_list => '')
323
+ @taggable.cached_tag_list.should be_blank
324
+ end
325
+
326
+ it 'should have a tag list' do
327
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
328
+ @taggable = CachedModel.find(@taggable.id)
329
+ @taggable.tag_list.sort.should == %w(awesome epic).sort
330
+ end
331
+
332
+ it 'should keep the tag list' do
333
+ @taggable.update_attributes(:tag_list => 'awesome, epic')
334
+ @taggable = CachedModel.find(@taggable.id)
335
+ @taggable.save!
336
+ @taggable.tag_list.sort.should == %w(awesome epic).sort
337
+ end
338
+ end
339
+
340
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tagtical
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.6
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Aryk Grosz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-07-10 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sqlite3-ruby
16
+ requirement: &2161646020 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2161646020
25
+ - !ruby/object:Gem::Dependency
26
+ name: mysql
27
+ requirement: &2161645540 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2161645540
36
+ - !ruby/object:Gem::Dependency
37
+ name: jeweler
38
+ requirement: &2161645060 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2161645060
47
+ - !ruby/object:Gem::Dependency
48
+ name: rcov
49
+ requirement: &2161644580 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *2161644580
58
+ description: Tagtical allows you do create subclasses for Tag and add additional functionality
59
+ in an STI fashion. For example. You could do Tag::Color.find_by_name('blue').to_rgb.
60
+ It also supports storing weights or relevance on the taggings.
61
+ email: aryk@mixbook.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files:
65
+ - README.rdoc
66
+ files:
67
+ - CHANGELOG
68
+ - Gemfile
69
+ - Gemfile.lock
70
+ - MIT-LICENSE
71
+ - README.rdoc
72
+ - Rakefile
73
+ - VERSION
74
+ - generators/tagtical_migration/tagtical_migration_generator.rb
75
+ - generators/tagtical_migration/templates/migration.rb
76
+ - lib/generators/tagtical/migration/migration_generator.rb
77
+ - lib/generators/tagtical/migration/templates/active_record/migration.rb
78
+ - lib/tagtical.rb
79
+ - lib/tagtical/acts_as_tagger.rb
80
+ - lib/tagtical/compatibility/Gemfile
81
+ - lib/tagtical/compatibility/active_record_backports.rb
82
+ - lib/tagtical/tag.rb
83
+ - lib/tagtical/tag_list.rb
84
+ - lib/tagtical/taggable.rb
85
+ - lib/tagtical/taggable/cache.rb
86
+ - lib/tagtical/taggable/collection.rb
87
+ - lib/tagtical/taggable/core.rb
88
+ - lib/tagtical/taggable/ownership.rb
89
+ - lib/tagtical/taggable/related.rb
90
+ - lib/tagtical/tagging.rb
91
+ - lib/tagtical/tags_helper.rb
92
+ - rails/init.rb
93
+ - spec/bm.rb
94
+ - spec/database.yml
95
+ - spec/database.yml.sample
96
+ - spec/models.rb
97
+ - spec/schema.rb
98
+ - spec/spec_helper.rb
99
+ - spec/tagtical/acts_as_tagger_spec.rb
100
+ - spec/tagtical/tag_list_spec.rb
101
+ - spec/tagtical/tag_spec.rb
102
+ - spec/tagtical/taggable_spec.rb
103
+ - spec/tagtical/tagger_spec.rb
104
+ - spec/tagtical/tagging_spec.rb
105
+ - spec/tagtical/tags_helper_spec.rb
106
+ - spec/tagtical/tagtical_spec.rb
107
+ homepage: https://github.com/Mixbook/tagtical
108
+ licenses: []
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 1.8.5
128
+ signing_key:
129
+ specification_version: 3
130
+ summary: Tagtical is a tagging plugin for Rails that provides weighting, contexts,
131
+ and inheritance for tags.
132
+ test_files: []