acts-as-taggable-on 2.0.0.pre5 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/{spec/spec.opts → .rspec} +0 -0
  4. data/.travis.yml +40 -0
  5. data/Appraisals +16 -0
  6. data/CHANGELOG.md +208 -0
  7. data/CONTRIBUTING.md +44 -0
  8. data/Gemfile +10 -5
  9. data/Guardfile +5 -0
  10. data/{MIT-LICENSE → LICENSE.md} +1 -1
  11. data/README.md +477 -0
  12. data/Rakefile +14 -52
  13. data/UPGRADING.md +8 -0
  14. data/acts-as-taggable-on.gemspec +36 -0
  15. data/{lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb → db/migrate/1_acts_as_taggable_on_migration.rb} +5 -3
  16. data/db/migrate/2_add_missing_unique_indices.rb +19 -0
  17. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +14 -0
  18. data/db/migrate/4_add_missing_taggable_index.rb +9 -0
  19. data/db/migrate/5_change_collation_for_tag_names.rb +9 -0
  20. data/gemfiles/activerecord_3.2.gemfile +15 -0
  21. data/gemfiles/activerecord_4.0.gemfile +15 -0
  22. data/gemfiles/activerecord_4.1.gemfile +15 -0
  23. data/gemfiles/activerecord_4.2.gemfile +16 -0
  24. data/lib/acts-as-taggable-on.rb +117 -22
  25. data/lib/acts_as_taggable_on/compatibility.rb +35 -0
  26. data/lib/acts_as_taggable_on/default_parser.rb +79 -0
  27. data/lib/acts_as_taggable_on/engine.rb +5 -0
  28. data/lib/acts_as_taggable_on/generic_parser.rb +19 -0
  29. data/lib/acts_as_taggable_on/tag.rb +137 -61
  30. data/lib/acts_as_taggable_on/tag_list.rb +96 -75
  31. data/lib/acts_as_taggable_on/tag_list_parser.rb +21 -0
  32. data/lib/acts_as_taggable_on/taggable/cache.rb +86 -0
  33. data/lib/acts_as_taggable_on/taggable/collection.rb +178 -0
  34. data/lib/acts_as_taggable_on/taggable/core.rb +459 -0
  35. data/lib/acts_as_taggable_on/taggable/dirty.rb +36 -0
  36. data/lib/acts_as_taggable_on/taggable/ownership.rb +125 -0
  37. data/lib/acts_as_taggable_on/taggable/related.rb +71 -0
  38. data/lib/acts_as_taggable_on/taggable.rb +102 -0
  39. data/lib/acts_as_taggable_on/tagger.rb +88 -0
  40. data/lib/acts_as_taggable_on/tagging.rb +38 -18
  41. data/lib/acts_as_taggable_on/tags_helper.rb +12 -14
  42. data/lib/acts_as_taggable_on/utils.rb +38 -0
  43. data/lib/acts_as_taggable_on/version.rb +4 -0
  44. data/lib/acts_as_taggable_on.rb +6 -0
  45. data/lib/tasks/tags_collate_utf8.rake +21 -0
  46. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +205 -195
  47. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +79 -81
  48. data/spec/acts_as_taggable_on/caching_spec.rb +83 -0
  49. data/spec/acts_as_taggable_on/default_parser_spec.rb +47 -0
  50. data/spec/acts_as_taggable_on/generic_parser_spec.rb +14 -0
  51. data/spec/acts_as_taggable_on/related_spec.rb +99 -0
  52. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +211 -0
  53. data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
  54. data/spec/acts_as_taggable_on/tag_list_spec.rb +142 -62
  55. data/spec/acts_as_taggable_on/tag_spec.rb +274 -64
  56. data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
  57. data/spec/acts_as_taggable_on/taggable_spec.rb +704 -181
  58. data/spec/acts_as_taggable_on/tagger_spec.rb +134 -56
  59. data/spec/acts_as_taggable_on/tagging_spec.rb +54 -22
  60. data/spec/acts_as_taggable_on/tags_helper_spec.rb +39 -22
  61. data/spec/acts_as_taggable_on/utils_spec.rb +23 -0
  62. data/spec/internal/app/models/altered_inheriting_taggable_model.rb +3 -0
  63. data/spec/internal/app/models/cached_model.rb +3 -0
  64. data/spec/internal/app/models/cached_model_with_array.rb +5 -0
  65. data/spec/internal/app/models/company.rb +15 -0
  66. data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
  67. data/spec/internal/app/models/market.rb +2 -0
  68. data/spec/internal/app/models/models.rb +90 -0
  69. data/spec/internal/app/models/non_standard_id_taggable_model.rb +8 -0
  70. data/spec/internal/app/models/ordered_taggable_model.rb +4 -0
  71. data/spec/internal/app/models/other_cached_model.rb +3 -0
  72. data/spec/internal/app/models/other_taggable_model.rb +4 -0
  73. data/spec/internal/app/models/student.rb +2 -0
  74. data/spec/internal/app/models/taggable_model.rb +13 -0
  75. data/spec/internal/app/models/untaggable_model.rb +3 -0
  76. data/spec/internal/app/models/user.rb +3 -0
  77. data/spec/internal/config/database.yml.sample +19 -0
  78. data/spec/internal/db/schema.rb +97 -0
  79. data/spec/spec_helper.rb +12 -38
  80. data/spec/support/0-helpers.rb +32 -0
  81. data/spec/support/array.rb +9 -0
  82. data/spec/support/database.rb +42 -0
  83. data/spec/support/database_cleaner.rb +21 -0
  84. metadata +268 -73
  85. data/CHANGELOG +0 -25
  86. data/README.rdoc +0 -212
  87. data/VERSION +0 -1
  88. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +0 -56
  89. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +0 -97
  90. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +0 -220
  91. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +0 -29
  92. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +0 -101
  93. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +0 -64
  94. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -41
  95. data/lib/acts_as_taggable_on/acts_as_tagger.rb +0 -47
  96. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -6
  97. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
  98. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +0 -31
  99. data/rails/init.rb +0 -1
  100. data/spec/bm.rb +0 -52
  101. data/spec/models.rb +0 -36
  102. data/spec/schema.rb +0 -42
@@ -1,114 +1,112 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
2
3
 
3
- describe "acts_as_tagger" do
4
- before(:each) do
5
- clean_database!
6
- end
7
-
8
- context "Tagger Method Generation" do
4
+ describe 'acts_as_tagger' do
5
+
6
+ describe 'Tagger Method Generation' do
9
7
  before(:each) do
10
- @tagger = TaggableUser.new()
8
+ @tagger = User.new
11
9
  end
12
10
 
13
- it "should add #is_tagger? query method to the class-side" do
14
- TaggableUser.should respond_to(:is_tagger?)
11
+ it 'should add #is_tagger? query method to the class-side' do
12
+ expect(User).to respond_to(:is_tagger?)
15
13
  end
16
-
17
- it "should return true from the class-side #is_tagger?" do
18
- TaggableUser.is_tagger?.should be_true
14
+
15
+ it 'should return true from the class-side #is_tagger?' do
16
+ expect(User.is_tagger?).to be_truthy
19
17
  end
20
-
21
- it "should return false from the base #is_tagger?" do
22
- ActiveRecord::Base.is_tagger?.should be_false
18
+
19
+ it 'should return false from the base #is_tagger?' do
20
+ expect(ActiveRecord::Base.is_tagger?).to be_falsy
23
21
  end
24
-
25
- it "should add #is_tagger? query method to the singleton" do
26
- @tagger.should respond_to(:is_tagger?)
22
+
23
+ it 'should add #is_tagger? query method to the singleton' do
24
+ expect(@tagger).to respond_to(:is_tagger?)
27
25
  end
28
-
29
- it "should add #tag method on the instance-side" do
30
- @tagger.should respond_to(:tag)
26
+
27
+ it 'should add #tag method on the instance-side' do
28
+ expect(@tagger).to respond_to(:tag)
31
29
  end
32
-
33
- it "should generate an association for #owned_taggings and #owned_tags" do
34
- @tagger.should respond_to(:owned_taggings, :owned_tags)
30
+
31
+ it 'should generate an association for #owned_taggings and #owned_tags' do
32
+ expect(@tagger).to respond_to(:owned_taggings, :owned_tags)
35
33
  end
36
34
  end
37
-
38
- describe "#tag" do
35
+
36
+ describe '#tag' do
39
37
  context 'when called with a non-existent tag context' do
40
38
  before(:each) do
41
- @tagger = TaggableUser.new()
42
- @taggable = TaggableModel.new(:name=>"Richard Prior")
39
+ @tagger = User.new
40
+ @taggable = TaggableModel.new(name: 'Richard Prior')
43
41
  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
42
+
43
+ it 'should by default not throw an exception ' do
44
+ expect(@taggable.tag_list_on(:foo)).to be_empty
45
+ expect(-> {
46
+ @tagger.tag(@taggable, with: 'this, and, that', on: :foo)
47
+ }).to_not raise_error
50
48
  end
51
-
49
+
52
50
  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')
51
+ expect(@taggable.tag_list_on(:here_ond_now)).to be_empty
52
+ @tagger.tag(@taggable, with: 'that', on: :here_ond_now)
53
+ expect(@taggable.tag_list_on(:here_ond_now)).to_not include('that')
54
+ expect(@taggable.all_tags_list_on(:here_ond_now)).to include('that')
57
55
  end
58
-
59
- it "should show all the tag list when both public and owned tags exist" do
56
+
57
+ it 'should show all the tag list when both public and owned tags exist' do
60
58
  @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
59
+ @tagger.tag(@taggable, with: 'java, lisp', on: :tags)
60
+ expect(@taggable.all_tags_on(:tags).map(&:name).sort).to eq(%w(ruby python java lisp).sort)
63
61
  end
64
-
65
- it "should not add owned tags to the common list" do
62
+
63
+ it 'should not add owned tags to the common list' do
66
64
  @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)
65
+ @tagger.tag(@taggable, with: 'java, lisp', on: :tags)
66
+ expect(@taggable.tag_list).to eq(%w(ruby python))
67
+ @tagger.tag(@taggable, with: '', on: :tags)
68
+ expect(@taggable.tag_list).to eq(%w(ruby python))
71
69
  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
70
+
71
+ it 'should throw an exception when the default is over-ridden' do
72
+ expect(@taggable.tag_list_on(:foo_boo)).to be_empty
73
+ expect(-> {
74
+ @tagger.tag(@taggable, with: 'this, and, that', on: :foo_boo, force: false)
75
+ }).to raise_error
78
76
  end
79
77
 
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
78
+ it 'should not create the tag context on-the-fly when the default is over-ridden' do
79
+ expect(@taggable.tag_list_on(:foo_boo)).to be_empty
80
+ @tagger.tag(@taggable, with: 'this, and, that', on: :foo_boo, force: false) rescue
81
+ expect(@taggable.tag_list_on(:foo_boo)).to be_empty
84
82
  end
85
83
  end
86
-
87
- context "when called by multiple tagger's" do
84
+
85
+ describe "when called by multiple tagger's" do
88
86
  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)
87
+ @user_x = User.create(name: 'User X')
88
+ @user_y = User.create(name: 'User Y')
89
+ @taggable = TaggableModel.create(name: 'acts_as_taggable_on', tag_list: 'plugin')
90
+
91
+ @user_x.tag(@taggable, with: 'ruby, rails', on: :tags)
92
+ @user_y.tag(@taggable, with: 'ruby, plugin', on: :tags)
93
+
94
+ @user_y.tag(@taggable, with: '', on: :tags)
95
+ @user_y.tag(@taggable, with: '', on: :tags)
98
96
  end
99
-
100
- it "should delete owned tags" do
101
- @user_y.owned_tags.should == []
97
+
98
+ it 'should delete owned tags' do
99
+ expect(@user_y.owned_tags).to be_empty
102
100
  end
103
-
104
- it "should not delete other taggers tags" do
105
- @user_x.owned_tags.should have(2).items
101
+
102
+ it 'should not delete other taggers tags' do
103
+ expect(@user_x.owned_tags.count).to eq(2)
106
104
  end
107
-
108
- it "should not delete original tags" do
109
- @taggable.all_tags_list_on(:tags).should include('plugin')
105
+
106
+ it 'should not delete original tags' do
107
+ expect(@taggable.all_tags_list_on(:tags)).to include('plugin')
110
108
  end
111
109
  end
112
110
  end
113
111
 
114
- end
112
+ end
@@ -0,0 +1,83 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe 'Acts As Taggable On' do
5
+
6
+ describe 'Caching' do
7
+ before(:each) do
8
+ @taggable = CachedModel.new(name: 'Bob Jones')
9
+ @another_taggable = OtherCachedModel.new(name: 'John Smith')
10
+ end
11
+
12
+ it 'should add saving of tag lists and cached tag lists to the instance' do
13
+ expect(@taggable).to respond_to(:save_cached_tag_list)
14
+ expect(@another_taggable).to respond_to(:save_cached_tag_list)
15
+
16
+ expect(@taggable).to respond_to(:save_tags)
17
+ end
18
+
19
+ it 'should add cached tag lists to the instance if cached column is not present' do
20
+ expect(TaggableModel.new(name: 'Art Kram')).to_not respond_to(:save_cached_tag_list)
21
+ end
22
+
23
+ it 'should generate a cached column checker for each tag type' do
24
+ expect(CachedModel).to respond_to(:caching_tag_list?)
25
+ expect(OtherCachedModel).to respond_to(:caching_language_list?)
26
+ end
27
+
28
+ it 'should not have cached tags' do
29
+ expect(@taggable.cached_tag_list).to be_blank
30
+ expect(@another_taggable.cached_language_list).to be_blank
31
+ end
32
+
33
+ it 'should cache tags' do
34
+ @taggable.update_attributes(tag_list: 'awesome, epic')
35
+ expect(@taggable.cached_tag_list).to eq('awesome, epic')
36
+
37
+ @another_taggable.update_attributes(language_list: 'ruby, .net')
38
+ expect(@another_taggable.cached_language_list).to eq('ruby, .net')
39
+ end
40
+
41
+ it 'should keep the cache' do
42
+ @taggable.update_attributes(tag_list: 'awesome, epic')
43
+ @taggable = CachedModel.find(@taggable.id)
44
+ @taggable.save!
45
+ expect(@taggable.cached_tag_list).to eq('awesome, epic')
46
+ end
47
+
48
+ it 'should update the cache' do
49
+ @taggable.update_attributes(tag_list: 'awesome, epic')
50
+ @taggable.update_attributes(tag_list: 'awesome')
51
+ expect(@taggable.cached_tag_list).to eq('awesome')
52
+ end
53
+
54
+ it 'should remove the cache' do
55
+ @taggable.update_attributes(tag_list: 'awesome, epic')
56
+ @taggable.update_attributes(tag_list: '')
57
+ expect(@taggable.cached_tag_list).to be_blank
58
+ end
59
+
60
+ it 'should have a tag list' do
61
+ @taggable.update_attributes(tag_list: 'awesome, epic')
62
+ @taggable = CachedModel.find(@taggable.id)
63
+ expect(@taggable.tag_list.sort).to eq(%w(awesome epic).sort)
64
+ end
65
+
66
+ it 'should keep the tag list' do
67
+ @taggable.update_attributes(tag_list: 'awesome, epic')
68
+ @taggable = CachedModel.find(@taggable.id)
69
+ @taggable.save!
70
+ expect(@taggable.tag_list.sort).to eq(%w(awesome epic).sort)
71
+ end
72
+
73
+ it 'should clear the cache on reset_column_information' do
74
+ CachedModel.column_names
75
+ CachedModel.reset_column_information
76
+ expect(CachedModel.instance_variable_get(:@acts_as_taggable_on_cache_columns)).to eql(nil)
77
+ end
78
+ end
79
+
80
+ describe 'CachingWithArray' do
81
+ pending '#TODO'
82
+ end
83
+ end
@@ -0,0 +1,47 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe ActsAsTaggableOn::DefaultParser do
5
+ it '#parse should return empty array if empty array is passed' do
6
+ parser = ActsAsTaggableOn::DefaultParser.new([])
7
+ expect(parser.parse).to be_empty
8
+ end
9
+
10
+ describe 'Multiple Delimiter' do
11
+ before do
12
+ @old_delimiter = ActsAsTaggableOn.delimiter
13
+ end
14
+
15
+ after do
16
+ ActsAsTaggableOn.delimiter = @old_delimiter
17
+ end
18
+
19
+ it 'should separate tags by delimiters' do
20
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
21
+ parser = ActsAsTaggableOn::DefaultParser.new('cool, data|I have')
22
+ expect(parser.parse.to_s).to eq('cool, data, I, have')
23
+ end
24
+
25
+ it 'should escape quote' do
26
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
27
+ parser = ActsAsTaggableOn::DefaultParser.new("'I have'|cool, data")
28
+ expect(parser.parse.to_s).to eq('"I have", cool, data')
29
+
30
+ parser = ActsAsTaggableOn::DefaultParser.new('"I, have"|cool, data')
31
+ expect(parser.parse.to_s).to eq('"I, have", cool, data')
32
+ end
33
+
34
+ it 'should work for utf8 delimiter and long delimiter' do
35
+ ActsAsTaggableOn.delimiter = [',', '的', '可能是']
36
+ parser = ActsAsTaggableOn::DefaultParser.new('我的东西可能是不见了,还好有备份')
37
+ expect(parser.parse.to_s).to eq('我, 东西, 不见了, 还好有备份')
38
+ end
39
+
40
+ it 'should work for multiple quoted tags' do
41
+ ActsAsTaggableOn.delimiter = [',']
42
+ parser = ActsAsTaggableOn::DefaultParser.new('"Ruby Monsters","eat Katzenzungen"')
43
+ expect(parser.parse.to_s).to eq('Ruby Monsters, eat Katzenzungen')
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,14 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe ActsAsTaggableOn::GenericParser do
5
+ it '#parse should return empty array if empty tag string is passed' do
6
+ tag_list = ActsAsTaggableOn::GenericParser.new('')
7
+ expect(tag_list.parse).to be_empty
8
+ end
9
+
10
+ it '#parse should separate tags by comma' do
11
+ tag_list = ActsAsTaggableOn::GenericParser.new('cool,data,,I,have')
12
+ expect(tag_list.parse).to eq(%w(cool data I have))
13
+ end
14
+ end
@@ -0,0 +1,99 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe 'Acts As Taggable On' do
5
+
6
+ describe 'Related Objects' do
7
+
8
+ #TODO, shared example
9
+ it 'should find related objects based on tag names on context' do
10
+ taggable1 = TaggableModel.create!(name: 'Taggable 1',tag_list: 'one, two')
11
+ taggable2 = TaggableModel.create!(name: 'Taggable 2',tag_list: 'three, four')
12
+ taggable3 = TaggableModel.create!(name: 'Taggable 3',tag_list: 'one, four')
13
+
14
+ expect(taggable1.find_related_tags).to include(taggable3)
15
+ expect(taggable1.find_related_tags).to_not include(taggable2)
16
+ end
17
+
18
+ it 'finds related tags for ordered taggable on' do
19
+ taggable1 = OrderedTaggableModel.create!(name: 'Taggable 1',colour_list: 'one, two')
20
+ taggable2 = OrderedTaggableModel.create!(name: 'Taggable 2',colour_list: 'three, four')
21
+ taggable3 = OrderedTaggableModel.create!(name: 'Taggable 3',colour_list: 'one, four')
22
+
23
+ expect(taggable1.find_related_colours).to include(taggable3)
24
+ expect(taggable1.find_related_colours).to_not include(taggable2)
25
+ end
26
+
27
+ it 'should find related objects based on tag names on context - non standard id' do
28
+ taggable1 = NonStandardIdTaggableModel.create!(name: 'Taggable 1',tag_list: 'one, two')
29
+ taggable2 = NonStandardIdTaggableModel.create!(name: 'Taggable 2',tag_list: 'three, four')
30
+ taggable3 = NonStandardIdTaggableModel.create!(name: 'Taggable 3',tag_list: 'one, four')
31
+
32
+ expect(taggable1.find_related_tags).to include(taggable3)
33
+ expect(taggable1.find_related_tags).to_not include(taggable2)
34
+ end
35
+
36
+ it 'should find other related objects based on tag names on context' do
37
+ taggable1 = TaggableModel.create!(name: 'Taggable 1',tag_list: 'one, two')
38
+ taggable2 = OtherTaggableModel.create!(name: 'Taggable 2',tag_list: 'three, four')
39
+ taggable3 = OtherTaggableModel.create!(name: 'Taggable 3',tag_list: 'one, four')
40
+
41
+ expect(taggable1.find_related_tags_for(OtherTaggableModel)).to include(taggable3)
42
+ expect(taggable1.find_related_tags_for(OtherTaggableModel)).to_not include(taggable2)
43
+ end
44
+
45
+ it 'should find other related objects based on tags only from particular context' do
46
+ taggable1 = TaggableModel.create!(name: 'Taggable 1',tag_list: 'one, two')
47
+ taggable2 = TaggableModel.create!(name: 'Taggable 2',tag_list: 'three, four', skill_list: 'one, two')
48
+ taggable3 = TaggableModel.create!(name: 'Taggable 3',tag_list: 'one, four')
49
+
50
+ expect(taggable1.find_related_tags).to include(taggable3)
51
+ expect(taggable1.find_related_tags).to_not include(taggable2)
52
+ end
53
+
54
+
55
+ shared_examples "a collection" do
56
+ it do
57
+ taggable1 = described_class.create!(name: 'Taggable 1', tag_list: 'one')
58
+ taggable2 = described_class.create!(name: 'Taggable 2', tag_list: 'one, two')
59
+
60
+ expect(taggable1.find_related_tags).to include(taggable2)
61
+ expect(taggable1.find_related_tags).to_not include(taggable1)
62
+ end
63
+ end
64
+
65
+ # it 'should not include the object itself in the list of related objects' do
66
+ describe TaggableModel do
67
+ it_behaves_like "a collection"
68
+ end
69
+
70
+ # it 'should not include the object itself in the list of related objects - non standard id' do
71
+ describe NonStandardIdTaggableModel do
72
+ it_behaves_like "a collection"
73
+ end
74
+
75
+ context 'Ignored Tags' do
76
+ let(:taggable1) { TaggableModel.create!(name: 'Taggable 1', tag_list: 'one, two, four') }
77
+ let(:taggable2) { TaggableModel.create!(name: 'Taggable 2', tag_list: 'two, three') }
78
+ let(:taggable3) { TaggableModel.create!(name: 'Taggable 3', tag_list: 'one, three') }
79
+
80
+ it 'should not include ignored tags in related search' do
81
+ expect(taggable1.find_related_tags(ignore: 'two')).to_not include(taggable2)
82
+ expect(taggable1.find_related_tags(ignore: 'two')).to include(taggable3)
83
+ end
84
+
85
+ it 'should accept array of ignored tags' do
86
+ taggable4 = TaggableModel.create!(name: 'Taggable 4', tag_list: 'four')
87
+
88
+
89
+ expect(taggable1.find_related_tags(ignore: ['two', 'four'])).to_not include(taggable2)
90
+ expect(taggable1.find_related_tags(ignore: ['two', 'four'])).to_not include(taggable4)
91
+ end
92
+
93
+ it 'should accept symbols as ignored tags' do
94
+ expect(taggable1.find_related_tags(ignore: :two)).to_not include(taggable2)
95
+ end
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,211 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe 'Single Table Inheritance' do
5
+ let(:taggable) { TaggableModel.new(name: 'taggable model') }
6
+
7
+ let(:inheriting_model) { InheritingTaggableModel.new(name: 'Inheriting Taggable Model') }
8
+ let(:altered_inheriting) { AlteredInheritingTaggableModel.new(name: 'Altered Inheriting Model') }
9
+
10
+ 1.upto(4) do |n|
11
+ let(:"inheriting_#{n}") { InheritingTaggableModel.new(name: "Inheriting Model #{n}") }
12
+ end
13
+
14
+ let(:student) { Student.create! }
15
+
16
+ describe 'tag contexts' do
17
+ it 'should pass on to STI-inherited models' do
18
+ expect(inheriting_model).to respond_to(:tag_list, :skill_list, :language_list)
19
+ expect(altered_inheriting).to respond_to(:tag_list, :skill_list, :language_list)
20
+ end
21
+
22
+ it 'should pass on to altered STI models' do
23
+ expect(altered_inheriting).to respond_to(:part_list)
24
+ end
25
+ end
26
+
27
+ context 'matching contexts' do
28
+
29
+ before do
30
+ inheriting_1.offering_list = 'one, two'
31
+ inheriting_1.need_list = 'one, two'
32
+ inheriting_1.save!
33
+
34
+ inheriting_2.need_list = 'one, two'
35
+ inheriting_2.save!
36
+
37
+ inheriting_3.offering_list = 'one, two'
38
+ inheriting_3.save!
39
+
40
+ inheriting_4.tag_list = 'one, two, three, four'
41
+ inheriting_4.save!
42
+
43
+ taggable.need_list = 'one, two'
44
+ taggable.save!
45
+ end
46
+
47
+ it 'should find objects with tags of matching contexts' do
48
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to include(inheriting_2)
49
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_3)
50
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_4)
51
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(taggable)
52
+
53
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(inheriting_2)
54
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_3)
55
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_4)
56
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(taggable)
57
+ end
58
+
59
+ it 'should not include the object itself in the list of related objects with tags of matching contexts' do
60
+ expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_1)
61
+ expect(inheriting_1.find_matching_contexts_for(InheritingTaggableModel, :offerings, :needs)).to_not include(inheriting_1)
62
+ expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_1)
63
+ end
64
+ end
65
+
66
+ context 'find related tags' do
67
+ before do
68
+ inheriting_1.tag_list = 'one, two'
69
+ inheriting_1.save
70
+
71
+ inheriting_2.tag_list = 'three, four'
72
+ inheriting_2.save
73
+
74
+ inheriting_3.tag_list = 'one, four'
75
+ inheriting_3.save
76
+
77
+ taggable.tag_list = 'one, two, three, four'
78
+ taggable.save
79
+ end
80
+
81
+ it 'should find related objects based on tag names on context' do
82
+ expect(inheriting_1.find_related_tags).to include(inheriting_3)
83
+ expect(inheriting_1.find_related_tags).to_not include(inheriting_2)
84
+ expect(inheriting_1.find_related_tags).to_not include(taggable)
85
+
86
+ expect(inheriting_1.find_related_tags_for(TaggableModel)).to include(inheriting_3)
87
+ expect(inheriting_1.find_related_tags_for(TaggableModel)).to_not include(inheriting_2)
88
+ expect(inheriting_1.find_related_tags_for(TaggableModel)).to include(taggable)
89
+ end
90
+
91
+ it 'should not include the object itself in the list of related objects' do
92
+ expect(inheriting_1.find_related_tags).to_not include(inheriting_1)
93
+ expect(inheriting_1.find_related_tags_for(InheritingTaggableModel)).to_not include(inheriting_1)
94
+ expect(inheriting_1.find_related_tags_for(TaggableModel)).to_not include(inheriting_1)
95
+ end
96
+ end
97
+
98
+ describe 'tag list' do
99
+ before do
100
+ @inherited_same = InheritingTaggableModel.new(name: 'inherited same')
101
+ @inherited_different = AlteredInheritingTaggableModel.new(name: 'inherited different')
102
+ end
103
+
104
+ #TODO, shared example
105
+ it 'should be able to save tags for inherited models' do
106
+ inheriting_model.tag_list = 'bob, kelso'
107
+ inheriting_model.save
108
+ expect(InheritingTaggableModel.tagged_with('bob').first).to eq(inheriting_model)
109
+ end
110
+
111
+ it 'should find STI tagged models on the superclass' do
112
+ inheriting_model.tag_list = 'bob, kelso'
113
+ inheriting_model.save
114
+ expect(TaggableModel.tagged_with('bob').first).to eq(inheriting_model)
115
+ end
116
+
117
+ it 'should be able to add on contexts only to some subclasses' do
118
+ altered_inheriting.part_list = 'fork, spoon'
119
+ altered_inheriting.save
120
+ expect(InheritingTaggableModel.tagged_with('fork', on: :parts)).to be_empty
121
+ expect(AlteredInheritingTaggableModel.tagged_with('fork', on: :parts).first).to eq(altered_inheriting)
122
+ end
123
+
124
+ it 'should have different tag_counts_on for inherited models' do
125
+ inheriting_model.tag_list = 'bob, kelso'
126
+ inheriting_model.save!
127
+ altered_inheriting.tag_list = 'fork, spoon'
128
+ altered_inheriting.save!
129
+
130
+ expect(InheritingTaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso))
131
+ expect(AlteredInheritingTaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(fork spoon))
132
+ expect(TaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso fork spoon))
133
+ end
134
+
135
+ it 'should have different tags_on for inherited models' do
136
+ inheriting_model.tag_list = 'bob, kelso'
137
+ inheriting_model.save!
138
+ altered_inheriting.tag_list = 'fork, spoon'
139
+ altered_inheriting.save!
140
+
141
+ expect(InheritingTaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso))
142
+ expect(AlteredInheritingTaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(fork spoon))
143
+ expect(TaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso fork spoon))
144
+ end
145
+
146
+ it 'should store same tag without validation conflict' do
147
+ taggable.tag_list = 'one'
148
+ taggable.save!
149
+
150
+ inheriting_model.tag_list = 'one'
151
+ inheriting_model.save!
152
+
153
+ inheriting_model.update_attributes! name: 'foo'
154
+ end
155
+ end
156
+
157
+ describe 'ownership' do
158
+ it 'should have taggings' do
159
+ student.tag(taggable, with: 'ruby,scheme', on: :tags)
160
+ expect(student.owned_taggings.count).to eq(2)
161
+ end
162
+
163
+ it 'should have tags' do
164
+ student.tag(taggable, with: 'ruby,scheme', on: :tags)
165
+ expect(student.owned_tags.count).to eq(2)
166
+ end
167
+
168
+ it 'should return tags for the inheriting tagger' do
169
+ student.tag(taggable, with: 'ruby, scheme', on: :tags)
170
+ expect(taggable.tags_from(student)).to eq(%w(ruby scheme))
171
+ end
172
+
173
+ it 'returns owner tags on the tagger' do
174
+ student.tag(taggable, with: 'ruby, scheme', on: :tags)
175
+ expect(taggable.owner_tags_on(student, :tags).count).to eq(2)
176
+ end
177
+
178
+ it 'should scope objects returned by tagged_with by owners' do
179
+ student.tag(taggable, with: 'ruby, scheme', on: :tags)
180
+ expect(TaggableModel.tagged_with(%w(ruby scheme), owned_by: student).count).to eq(1)
181
+ end
182
+ end
183
+
184
+ describe 'a subclass of Tag' do
185
+ let(:company) { Company.new(:name => 'Dewey, Cheatham & Howe') }
186
+ let(:user) { User.create! }
187
+
188
+ subject { Market.create! :name => 'finance' }
189
+
190
+ its(:type) { should eql 'Market' }
191
+
192
+ it 'sets STI type through string list' do
193
+ company.market_list = 'law, accounting'
194
+ company.save!
195
+ expect(Market.count).to eq(2)
196
+ end
197
+
198
+ it 'does not interfere with a normal Tag context on the same model' do
199
+ company.location_list = 'cambridge'
200
+ company.save!
201
+ expect(ActsAsTaggableOn::Tag.where(name: 'cambridge', type: nil)).to_not be_empty
202
+ end
203
+
204
+ it 'is returned with proper type through ownership' do
205
+ user.tag(company, :with => 'ripoffs, rackets', :on => :markets)
206
+ tags = company.owner_tags_on(user, :markets)
207
+ expect(tags.all? { |tag| tag.is_a? Market }).to be_truthy
208
+ end
209
+ end
210
+ end
211
+