taxonomy 0.0.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 (68) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +28 -0
  3. data/Rakefile +31 -0
  4. data/lib/generators/taxonomy/migration/migration_generator.rb +39 -0
  5. data/lib/generators/taxonomy/migration/templates/active_record/migration.rb +36 -0
  6. data/lib/tasks/taxonomy_tasks.rake +4 -0
  7. data/lib/taxonomy.rb +25 -0
  8. data/lib/taxonomy/group_helper.rb +12 -0
  9. data/lib/taxonomy/has_tagger.rb +52 -0
  10. data/lib/taxonomy/has_taxonomy.rb +502 -0
  11. data/lib/taxonomy/tag.rb +485 -0
  12. data/lib/taxonomy/tag_list.rb +97 -0
  13. data/lib/taxonomy/tagging.rb +12 -0
  14. data/lib/taxonomy/tags_helper.rb +13 -0
  15. data/lib/taxonomy/version.rb +3 -0
  16. data/spec/dummy/Rakefile +7 -0
  17. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  18. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  19. data/spec/dummy/app/models/altered_inheriting_taggable_model.rb +3 -0
  20. data/spec/dummy/app/models/inheriting_taggable_model.rb +2 -0
  21. data/spec/dummy/app/models/other_taggable_model.rb +4 -0
  22. data/spec/dummy/app/models/post.rb +2 -0
  23. data/spec/dummy/app/models/taggable_model.rb +6 -0
  24. data/spec/dummy/app/models/taggable_user.rb +3 -0
  25. data/spec/dummy/app/models/treed_model.rb +3 -0
  26. data/spec/dummy/app/models/untaggable_model.rb +2 -0
  27. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  28. data/spec/dummy/config.ru +4 -0
  29. data/spec/dummy/config/application.rb +45 -0
  30. data/spec/dummy/config/boot.rb +10 -0
  31. data/spec/dummy/config/database.yml +19 -0
  32. data/spec/dummy/config/environment.rb +5 -0
  33. data/spec/dummy/config/environments/development.rb +30 -0
  34. data/spec/dummy/config/environments/production.rb +60 -0
  35. data/spec/dummy/config/environments/test.rb +39 -0
  36. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  37. data/spec/dummy/config/initializers/inflections.rb +10 -0
  38. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  39. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  40. data/spec/dummy/config/initializers/session_store.rb +8 -0
  41. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  42. data/spec/dummy/config/locales/en.yml +5 -0
  43. data/spec/dummy/config/routes.rb +58 -0
  44. data/spec/dummy/db/migrate/20111221004133_create_posts.rb +8 -0
  45. data/spec/dummy/db/migrate/20111221023928_taxonomy_migration.rb +35 -0
  46. data/spec/dummy/db/migrate/20111221024100_create_bulk.rb +18 -0
  47. data/spec/dummy/db/schema.rb +65 -0
  48. data/spec/dummy/db/test.sqlite3 +0 -0
  49. data/spec/dummy/log/test.log +100915 -0
  50. data/spec/dummy/public/404.html +26 -0
  51. data/spec/dummy/public/422.html +26 -0
  52. data/spec/dummy/public/500.html +26 -0
  53. data/spec/dummy/public/favicon.ico +0 -0
  54. data/spec/dummy/script/rails +6 -0
  55. data/spec/factories/posts.rb +6 -0
  56. data/spec/generators/taxonomy/migration/migration_generator_spec.rb +22 -0
  57. data/spec/models/post_spec.rb +5 -0
  58. data/spec/spec_helper.rb +30 -0
  59. data/spec/taxonomy/group_helper_spec.rb +21 -0
  60. data/spec/taxonomy/has_tagger_spec.rb +113 -0
  61. data/spec/taxonomy/has_taxonomy_spec.rb +226 -0
  62. data/spec/taxonomy/tag_list_spec.rb +70 -0
  63. data/spec/taxonomy/tag_spec.rb +462 -0
  64. data/spec/taxonomy/taggable_spec.rb +262 -0
  65. data/spec/taxonomy/tagger_spec.rb +40 -0
  66. data/spec/taxonomy/tagging_spec.rb +25 -0
  67. data/spec/taxonomy/tags_helper_spec.rb +29 -0
  68. metadata +225 -0
@@ -0,0 +1,70 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe TagList do
4
+ before(:each) do
5
+ @tag_list = TagList.new("awesome","radical")
6
+ end
7
+
8
+ it "should be an array" do
9
+ @tag_list.is_a?(Array).should be_true
10
+ end
11
+
12
+ it "should be able to be add a new tag word" do
13
+ @tag_list.add("cool")
14
+ @tag_list.include?("cool").should be_true
15
+ end
16
+
17
+ it "should be able to add delimited lists of words" do
18
+ @tag_list.add("cool, wicked", :parse => true)
19
+ @tag_list.include?("cool").should be_true
20
+ @tag_list.include?("wicked").should be_true
21
+ end
22
+
23
+ it "should be able to add delimited list of words with quoted delimiters" do
24
+ @tag_list.add("'cool, wicked', \"really cool, really wicked\"", :parse => true)
25
+ @tag_list.include?("cool, wicked").should be_true
26
+ @tag_list.include?("really cool, really wicked").should be_true
27
+ end
28
+
29
+ it "should be able to handle other uses of quotation marks correctly" do
30
+ @tag_list.add("john's cool car, mary's wicked toy", :parse => true)
31
+ @tag_list.include?("john's cool car").should be_true
32
+ @tag_list.include?("mary's wicked toy").should be_true
33
+ end
34
+
35
+ it "should be able to add an array of words" do
36
+ @tag_list.add(["cool", "wicked"], :parse => true)
37
+ @tag_list.include?("cool").should be_true
38
+ @tag_list.include?("wicked").should be_true
39
+ end
40
+
41
+ it "should be able to remove words" do
42
+ @tag_list.remove("awesome")
43
+ @tag_list.include?("awesome").should be_false
44
+ end
45
+
46
+ it "should be able to remove delimited lists of words" do
47
+ @tag_list.remove("awesome, radical", :parse => true)
48
+ @tag_list.should be_empty
49
+ end
50
+
51
+ it "should be able to remove an array of words" do
52
+ @tag_list.remove(["awesome", "radical"], :parse => true)
53
+ @tag_list.should be_empty
54
+ end
55
+
56
+ it "should give a delimited list of words when converted to string" do
57
+ @tag_list.to_s.should == "awesome, radical"
58
+ end
59
+
60
+ it "should quote escape tags with commas in them" do
61
+ @tag_list.add("cool","rad,bodacious")
62
+ @tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
63
+ end
64
+
65
+ it "should be able to call to_s on a frozen tag list" do
66
+ @tag_list.freeze
67
+ lambda { @tag_list.add("cool","rad,bodacious") }.should raise_error
68
+ lambda { @tag_list.to_s }.should_not raise_error
69
+ end
70
+ end
@@ -0,0 +1,462 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Tag do
4
+ before(:each) do
5
+ # clean_database!
6
+ @tag = Tag.new
7
+ @user = TaggableModel.create(:name => "Pablo")
8
+ end
9
+
10
+ describe "named like any" do
11
+ before(:each) do
12
+ Tag.create(:context => "skills", :name => "awesome")
13
+ Tag.create(:context => "skills", :name => "epic")
14
+ end
15
+
16
+ it "should find both tags" do
17
+ Tag.named_like_any("skills", ["awesome", "epic"]).should have(2).items
18
+ end
19
+ end
20
+
21
+ describe "find or create by name" do
22
+ before(:each) do
23
+ @tag.name = "awesome"
24
+ @tag.save
25
+ end
26
+
27
+ it "should find by name" do
28
+ Tag.find_or_create_with_like_by_name("skills", "awesome").should == @tag
29
+ end
30
+
31
+ it "should find by name case insensitive" do
32
+ Tag.find_or_create_with_like_by_name("skills", "AWESOME").should == @tag
33
+ end
34
+
35
+ it "should create by name" do
36
+ lambda {
37
+ Tag.find_or_create_with_like_by_name("skills", "epic")
38
+ }.should change(Tag, :count).by(1)
39
+ end
40
+ end
41
+
42
+ describe "find or create all by any name" do
43
+ before(:each) do
44
+ @tag.name = "awesome"
45
+ @tag.context = "skills"
46
+ @tag.save
47
+ end
48
+
49
+ it "should find by name" do
50
+ Tag.find_or_create_all_with_like_by_name("skills", "awesome").should == [@tag]
51
+ end
52
+
53
+ it "should find by name case insensitive" do
54
+ Tag.find_or_create_all_with_like_by_name("skills", "AWESOME").should == [@tag]
55
+ end
56
+
57
+ it "should create by name" do
58
+ lambda {
59
+ Tag.find_or_create_all_with_like_by_name("skills", "epic")
60
+ }.should change(Tag, :count).by(1)
61
+ end
62
+
63
+ it "should find or create by name" do
64
+ lambda {
65
+ Tag.find_or_create_all_with_like_by_name("skills", "awesome", "epic").map(&:name).should == ["awesome", "epic"]
66
+ }.should change(Tag, :count).by(1)
67
+ end
68
+
69
+ it "should return an empty array if no tags are specified" do
70
+ Tag.find_or_create_all_with_like_by_name("skills", []).should == []
71
+ end
72
+ end
73
+
74
+ it "should require a name" do
75
+ @tag.valid?
76
+ @tag.errors[:name].should == ["can't be blank"]
77
+ @tag.name = "something"
78
+ @tag.valid?
79
+ @tag.errors[:name].should be_blank
80
+ end
81
+
82
+ it "should equal a tag with the same name" do
83
+ @tag.name = "awesome"
84
+ new_tag = Tag.new(:name => "awesome")
85
+ new_tag.should == @tag
86
+ end
87
+
88
+ it "should return its name when to_s is called" do
89
+ @tag.name = "cool"
90
+ @tag.to_s.should == "cool"
91
+ end
92
+
93
+ it "have scope named(context, something)" do
94
+ @tag.name = "cool"
95
+ @tag.context = "foo"
96
+ @tag.save!
97
+ Tag.named('foo', 'cool').should include(@tag)
98
+ end
99
+
100
+ it "have scope named_like(something)" do
101
+ @tag.name = "cool"
102
+ @tag.context = "foo"
103
+ @tag.save!
104
+ @another_tag = Tag.create!(:context => "foo", :name => "coolip")
105
+ Tag.named_like('foo', 'cool').should include(@tag, @another_tag)
106
+ end
107
+
108
+ describe "treed tags" do
109
+ before(:each) do
110
+ [TreedModel, Tag, Tagging].each(&:delete_all)
111
+ @taggable = TreedModel.new(:name => "Tyler Durden")
112
+ @taggable.save
113
+ end
114
+
115
+ it "should respond to treed_taggings_for" do
116
+ Tag.should respond_to(:treed_taggings_for)
117
+ end
118
+
119
+ it "should have scope named treed_taggings_for(categories)" do
120
+ @tag.name = "foo"
121
+ @tag.save
122
+ @taggable.category_list = "foo, bar"
123
+ @taggable.save
124
+ Tag.treed_taggings_for(:categories).all.should include(@tag)
125
+ end
126
+
127
+ it "should on creation, set automatically lft and rgt to the end of the tree" do
128
+ t = Tag.create(:name => "Movies")
129
+ t.save
130
+ @taggable.category_list = "FightClub"
131
+ @taggable.save
132
+ t = Tag.find_by_name("FightClub")
133
+ t.lft.should == Tag.maximum(:lft)
134
+ t.rgt.should == Tag.maximum(:rgt)
135
+ end
136
+
137
+ it "should move to child of" do
138
+ t = Tag.create(:name => "Movies")
139
+ t.save
140
+ @taggable.category_list = "FightClub"
141
+ @taggable.save
142
+ fc = Tag.find_by_name("FightClub")
143
+ fc.move_to_child_of(t.id)
144
+ fc.parent.should == t
145
+ end
146
+
147
+ it "should find self and descendants" do
148
+ t = Tag.create(:name => "Trees")
149
+ t.save
150
+ f = Tag.create(:name => "Fern")
151
+ f.save
152
+ f.move_to_child_of(t.id)
153
+ t.self_and_descendants.all.should == [t,f]
154
+ end
155
+
156
+ it "should find descendants" do
157
+ t = Tag.create(:name => "Trees")
158
+ t.save
159
+ f = Tag.create(:name => "Fern")
160
+ f.save
161
+ f.move_to_child_of(t.id)
162
+ t.self_and_descendants.all.should == [t,f]
163
+ t.descendants.all.should == [f]
164
+ end
165
+
166
+ it "should destroy descendants" do
167
+ t = Tag.create(:name => "Trees")
168
+ t.save
169
+ @taggable.category_list = "Fern"
170
+ @taggable.save
171
+ f = Tag.find_by_name("Fern")
172
+ f.move_to_child_of(t.id)
173
+ t.destroy
174
+ Tag.find_by_name("Fern").should be_nil
175
+ end
176
+
177
+ it "should find roots and leaves without overlap" do
178
+ t = Tag.create(:name => "Books")
179
+ t.save
180
+ @taggable.category_list = "One, Two"
181
+ @taggable.save
182
+ @taggable.categories.each do |cat|
183
+ cat.move_to_child_of(t.id)
184
+ cat.save
185
+ end
186
+ Tag.leaves.should_not include(t)
187
+ end
188
+
189
+ it "should find leaves" do
190
+ t = Tag.create(:name => "Books")
191
+ t.save
192
+ @taggable.category_list = "BendersGame"
193
+ @taggable.save
194
+ cat = Tag.find_by_name("BendersGame")
195
+ cat.move_to_child_of(t.id)
196
+ cat.should be_leaf
197
+ end
198
+
199
+ it "should return child correctly" do
200
+ t = Tag.create(:name => "Games")
201
+ t.save
202
+ @taggable.category_list = "Shooter"
203
+ @taggable.save
204
+ cat = Tag.find_by_name("Shooter")
205
+ cat.move_to_child_of(t.id)
206
+ cat.should be_child
207
+ t.should_not be_child
208
+ end
209
+
210
+ it "should default to root" do
211
+ t = Tag.create(:name => "Gum")
212
+ t.save
213
+ t.should be_root
214
+ end
215
+
216
+ it "should find self and ancestors when two deep" do
217
+ r = Tag.create(:name => "Root")
218
+ r.save
219
+ s = Tag.create(:name => "otherRoot")
220
+ s.save
221
+ c = Tag.create(:name => "child")
222
+ c.save
223
+ c.move_to_child_of(r.id)
224
+ c.self_and_ancestors.all.should == [r,c]
225
+ end
226
+
227
+ it "should find self and siblings with two siblings" do
228
+ root = Tag.create(:name => "root")
229
+ root.save
230
+ l = Tag.create(:name => "left")
231
+ l.save
232
+ r = Tag.create(:name => "right")
233
+ r.save
234
+ l.move_to_child_of(root.id)
235
+ r.move_to_child_of(root.id)
236
+ r.self_and_siblings.all.should == [l,r]
237
+ end
238
+
239
+ it "should find ancestors with self" do
240
+ root = Tag.create(:name => "root")
241
+ root.save
242
+ child = Tag.create(:name => "child")
243
+ child.save
244
+ child.move_to_child_of(root.id)
245
+ child.self_and_ancestors.all.should == [root, child]
246
+ end
247
+
248
+ it "should find ancestors without self" do
249
+ root = Tag.create(:name => "root")
250
+ root.save
251
+ child = Tag.create(:name => "child")
252
+ child.save
253
+ child.move_to_child_of(root.id)
254
+ child.ancestors.all.should == [root]
255
+ end
256
+
257
+ it "should find siblings without self" do
258
+ root = Tag.create(:name => "parent")
259
+ root.save
260
+ l = Tag.create(:name => "child1")
261
+ l.save
262
+ r = Tag.create(:name => "child2")
263
+ r.save
264
+ l.move_to_child_of(root.id)
265
+ r.move_to_child_of(root.id)
266
+ r.siblings.all.should == [l]
267
+ l.siblings.all.should == [r]
268
+ end
269
+
270
+ it "should find leaf nodes" do
271
+ root = Tag.create(:name => "root")
272
+ root.save
273
+ l = Tag.create(:name => "left")
274
+ l.save
275
+ r = Tag.create(:name => "right")
276
+ r.save
277
+ l.move_to_child_of(root.id)
278
+ r.move_to_child_of(root.id)
279
+ root.leaves.all.should == [l,r]
280
+ end
281
+
282
+ it "should count level 0" do
283
+ a = Tag.create(:name => "one")
284
+ a.save
285
+ a.level.should == 0
286
+ end
287
+
288
+ it "should count level 1 and 2" do
289
+ a = Tag.create(:name => "one")
290
+ a.save
291
+ b = Tag.create(:name => "b")
292
+ b.move_to_child_of(a.id)
293
+ c = Tag.create(:name => "c")
294
+ c.move_to_child_of(b.id)
295
+
296
+ b.level.should == 1
297
+ c.level.should == 2
298
+ end
299
+
300
+ describe "sibblings" do
301
+ before(:each) do
302
+ root = Tag.create(:name => "root")
303
+ root.save
304
+ @b = Tag.create(:name => "b")
305
+ @b.move_to_child_of(root.id)
306
+ @c = Tag.create(:name => "c")
307
+ @c.move_to_child_of(root.id)
308
+ end
309
+
310
+ it "left_siblings should find left siblings" do
311
+ @c.left_sibling.should == @b
312
+ end
313
+
314
+ it "right_siblings should find right siblings" do
315
+ @b.right_sibling.should == @c
316
+ end
317
+
318
+ it "left_siblings should return nil when no left sibling" do
319
+ @b.left_sibling.should be_nil
320
+ end
321
+
322
+ it "right_siblings should return nil when no right sibling" do
323
+ @c.right_sibling.should be_nil
324
+ end
325
+
326
+ it "should move right of given tag" do
327
+ @b.move_to_right_of(@c)
328
+ @b.left_sibling.should == @c
329
+ end
330
+
331
+ it "should move left of given tag" do
332
+ @c.move_to_left_of(@b)
333
+ @c.right_sibling.should == @b
334
+ end
335
+
336
+ it "should move right" do
337
+ @b.move_right
338
+ @b.left_sibling.should == @c
339
+ end
340
+
341
+ it "should move left" do
342
+ @c.move_left
343
+ @c.right_sibling.should == @b
344
+ end
345
+ end
346
+
347
+ describe "with invalid tree" do
348
+ before(:each) do
349
+ @root = Tag.create(:name => "root")
350
+ @root.save
351
+ @b = Tag.create(:name => "b")
352
+ @b.move_to_child_of(@root.id)
353
+ @c = Tag.create(:name => "c")
354
+ @c.move_to_child_of(@root.id)
355
+ @orphan = Tag.create(:name => "orphan")
356
+ @orphan.save
357
+ end
358
+ it "should not be valid when lft arbitrarily set to zero" do
359
+ Tag.update_all "lft = 0", "id = #{@c.id}" # have to work to break things
360
+ Tag.should_not be_valid
361
+ end
362
+ it "rebuild! should make valid when lft arbitrarily set to zero" do
363
+ Tag.update_all "lft = 0", "id = #{@c.id}" # have to work to break things
364
+ Tag.rebuild!
365
+ Tag.should be_valid
366
+ end
367
+ it "should not be valid when parents wrongly set to nil" do
368
+ Tag.update_all "parent_id = null", "id = #{@c.id}"
369
+ Tag.should_not be_valid
370
+ end
371
+ it "rebuild! should make valid when parents wrongly set to nil" do
372
+ Tag.update_all "parent_id = null", "id = #{@c.id}"
373
+ Tag.rebuild!
374
+ Tag.should be_valid
375
+ end
376
+ end
377
+
378
+ describe "" do
379
+ before(:each) do
380
+ @root = Tag.create(:name => "root")
381
+ @root.save
382
+ @b = Tag.create(:name => "b")
383
+ @b.move_to_child_of(@root.id)
384
+ @c = Tag.create(:name => "c")
385
+ @c.move_to_child_of(@root.id)
386
+ @orphan = Tag.create(:name => "orphan")
387
+ @orphan.save
388
+ end
389
+ describe "simple tree" do
390
+ it "should be letf and right valid" do
391
+ Tag.should be_left_and_rights_valid
392
+ end
393
+ it "should not have duplicates in the left and right columns" do
394
+ Tag.should be_no_duplicates_for_columns
395
+ end
396
+ it "should have all valid roots" do
397
+ Tag.should be_all_roots_valid
398
+ end
399
+ it "should be valid" do
400
+ Tag.should be_valid
401
+ end
402
+ end
403
+ describe "descendant" do
404
+ it "is_descendant_of should find descendant" do
405
+ @b.is_descendant_of?(@root).should == true
406
+ end
407
+
408
+ it "is_descendant_of should not find descendant when its self" do
409
+ @root.is_descendant_of?(@root).should == false
410
+ end
411
+
412
+ it "is_descendant_of should not find descendant when its orphan" do
413
+ @orphan.is_descendant_of?(@root).should == false
414
+ end
415
+
416
+ it "is_or_is_descendant_of should find descendant" do
417
+ @b.is_or_is_descendant_of?(@root).should == true
418
+ end
419
+
420
+ it "is_or_is_descendant_of sholud find self" do
421
+ @root.is_or_is_descendant_of?(@root).should == true
422
+ end
423
+
424
+ it "is_or_is_descendant_of should not find descendant when its orphan" do
425
+ @orphan.is_or_is_descendant_of?(@root).should == false
426
+ end
427
+ end
428
+ describe "ancestor" do
429
+ it "is_ancestor_of should find ancestor" do
430
+ @root.is_ancestor_of?(@b).should == true
431
+ end
432
+
433
+ it "is_ancestor_of should not find ancestor when its an orphan" do
434
+ @orphan.is_ancestor_of?(@root).should == false
435
+ end
436
+
437
+ it "is_or_is_ancestor_of should find ancestor" do
438
+ @root.is_or_is_ancestor_of?(@b).should == true
439
+ end
440
+
441
+ it "is_or_is_ancestor_of should not find ancestor when its an orphan" do
442
+ @orphan.is_or_is_ancestor_of?(@root).should == false
443
+ end
444
+
445
+ it "is_or_is_ancestor_of should find self" do
446
+ @root.is_or_is_ancestor_of?(@root).should == true
447
+ end
448
+ end
449
+ end
450
+ end
451
+
452
+ describe "without tree" do
453
+ before(:each) do
454
+ [TaggableModel, Tag, Tagging].each(&:delete_all)
455
+ @taggable = TaggableModel.new(:name => "Bob Jones")
456
+ end
457
+
458
+ # it "should not add lft, rgt or parent id for untreed contexts" do
459
+ # fail
460
+ # end
461
+ end
462
+ end