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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/{spec/spec.opts → .rspec} +0 -0
- data/.travis.yml +40 -0
- data/Appraisals +16 -0
- data/CHANGELOG.md +208 -0
- data/CONTRIBUTING.md +44 -0
- data/Gemfile +10 -5
- data/Guardfile +5 -0
- data/{MIT-LICENSE → LICENSE.md} +1 -1
- data/README.md +477 -0
- data/Rakefile +14 -52
- data/UPGRADING.md +8 -0
- data/acts-as-taggable-on.gemspec +36 -0
- data/{lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb → db/migrate/1_acts_as_taggable_on_migration.rb} +5 -3
- data/db/migrate/2_add_missing_unique_indices.rb +19 -0
- data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +14 -0
- data/db/migrate/4_add_missing_taggable_index.rb +9 -0
- data/db/migrate/5_change_collation_for_tag_names.rb +9 -0
- data/gemfiles/activerecord_3.2.gemfile +15 -0
- data/gemfiles/activerecord_4.0.gemfile +15 -0
- data/gemfiles/activerecord_4.1.gemfile +15 -0
- data/gemfiles/activerecord_4.2.gemfile +16 -0
- data/lib/acts-as-taggable-on.rb +117 -22
- data/lib/acts_as_taggable_on/compatibility.rb +35 -0
- data/lib/acts_as_taggable_on/default_parser.rb +79 -0
- data/lib/acts_as_taggable_on/engine.rb +5 -0
- data/lib/acts_as_taggable_on/generic_parser.rb +19 -0
- data/lib/acts_as_taggable_on/tag.rb +137 -61
- data/lib/acts_as_taggable_on/tag_list.rb +96 -75
- data/lib/acts_as_taggable_on/tag_list_parser.rb +21 -0
- data/lib/acts_as_taggable_on/taggable/cache.rb +86 -0
- data/lib/acts_as_taggable_on/taggable/collection.rb +178 -0
- data/lib/acts_as_taggable_on/taggable/core.rb +459 -0
- data/lib/acts_as_taggable_on/taggable/dirty.rb +36 -0
- data/lib/acts_as_taggable_on/taggable/ownership.rb +125 -0
- data/lib/acts_as_taggable_on/taggable/related.rb +71 -0
- data/lib/acts_as_taggable_on/taggable.rb +102 -0
- data/lib/acts_as_taggable_on/tagger.rb +88 -0
- data/lib/acts_as_taggable_on/tagging.rb +38 -18
- data/lib/acts_as_taggable_on/tags_helper.rb +12 -14
- data/lib/acts_as_taggable_on/utils.rb +38 -0
- data/lib/acts_as_taggable_on/version.rb +4 -0
- data/lib/acts_as_taggable_on.rb +6 -0
- data/lib/tasks/tags_collate_utf8.rake +21 -0
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +205 -195
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +79 -81
- data/spec/acts_as_taggable_on/caching_spec.rb +83 -0
- data/spec/acts_as_taggable_on/default_parser_spec.rb +47 -0
- data/spec/acts_as_taggable_on/generic_parser_spec.rb +14 -0
- data/spec/acts_as_taggable_on/related_spec.rb +99 -0
- data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +211 -0
- data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +142 -62
- data/spec/acts_as_taggable_on/tag_spec.rb +274 -64
- data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
- data/spec/acts_as_taggable_on/taggable_spec.rb +704 -181
- data/spec/acts_as_taggable_on/tagger_spec.rb +134 -56
- data/spec/acts_as_taggable_on/tagging_spec.rb +54 -22
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +39 -22
- data/spec/acts_as_taggable_on/utils_spec.rb +23 -0
- data/spec/internal/app/models/altered_inheriting_taggable_model.rb +3 -0
- data/spec/internal/app/models/cached_model.rb +3 -0
- data/spec/internal/app/models/cached_model_with_array.rb +5 -0
- data/spec/internal/app/models/company.rb +15 -0
- data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
- data/spec/internal/app/models/market.rb +2 -0
- data/spec/internal/app/models/models.rb +90 -0
- data/spec/internal/app/models/non_standard_id_taggable_model.rb +8 -0
- data/spec/internal/app/models/ordered_taggable_model.rb +4 -0
- data/spec/internal/app/models/other_cached_model.rb +3 -0
- data/spec/internal/app/models/other_taggable_model.rb +4 -0
- data/spec/internal/app/models/student.rb +2 -0
- data/spec/internal/app/models/taggable_model.rb +13 -0
- data/spec/internal/app/models/untaggable_model.rb +3 -0
- data/spec/internal/app/models/user.rb +3 -0
- data/spec/internal/config/database.yml.sample +19 -0
- data/spec/internal/db/schema.rb +97 -0
- data/spec/spec_helper.rb +12 -38
- data/spec/support/0-helpers.rb +32 -0
- data/spec/support/array.rb +9 -0
- data/spec/support/database.rb +42 -0
- data/spec/support/database_cleaner.rb +21 -0
- metadata +268 -73
- data/CHANGELOG +0 -25
- data/README.rdoc +0 -212
- data/VERSION +0 -1
- data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +0 -56
- data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +0 -97
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +0 -220
- data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +0 -29
- data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +0 -101
- data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +0 -64
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -41
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +0 -47
- data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -6
- data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
- data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +0 -31
- data/rails/init.rb +0 -1
- data/spec/bm.rb +0 -52
- data/spec/models.rb +0 -36
- data/spec/schema.rb +0 -42
@@ -1,114 +1,112 @@
|
|
1
|
-
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
2
3
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
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 =
|
8
|
+
@tagger = User.new
|
11
9
|
end
|
12
10
|
|
13
|
-
it
|
14
|
-
|
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
|
18
|
-
|
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
|
22
|
-
ActiveRecord::Base.is_tagger
|
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
|
26
|
-
@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
|
30
|
-
@tagger.
|
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
|
34
|
-
@tagger.
|
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
|
35
|
+
|
36
|
+
describe '#tag' do
|
39
37
|
context 'when called with a non-existent tag context' do
|
40
38
|
before(:each) do
|
41
|
-
@tagger =
|
42
|
-
@taggable = TaggableModel.new(:
|
39
|
+
@tagger = User.new
|
40
|
+
@taggable = TaggableModel.new(name: 'Richard Prior')
|
43
41
|
end
|
44
|
-
|
45
|
-
it
|
46
|
-
@taggable.tag_list_on(:foo).
|
47
|
-
|
48
|
-
@tagger.tag(@taggable, :
|
49
|
-
}.
|
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).
|
54
|
-
@tagger.tag(@taggable, :
|
55
|
-
@taggable.tag_list_on(:here_ond_now).
|
56
|
-
@taggable.all_tags_list_on(:here_ond_now).
|
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
|
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, :
|
62
|
-
@taggable.all_tags_on(:tags).map(&:name).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
|
62
|
+
|
63
|
+
it 'should not add owned tags to the common list' do
|
66
64
|
@taggable.tag_list = 'ruby, python'
|
67
|
-
@tagger.tag(@taggable, :
|
68
|
-
@taggable.tag_list.
|
69
|
-
@tagger.tag(@taggable, :
|
70
|
-
@taggable.tag_list.
|
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
|
74
|
-
@taggable.tag_list_on(:foo_boo).
|
75
|
-
|
76
|
-
@tagger.tag(@taggable, :
|
77
|
-
}.
|
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
|
81
|
-
@taggable.tag_list_on(:foo_boo).
|
82
|
-
@tagger.tag(@taggable, :
|
83
|
-
|
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
|
-
|
84
|
+
|
85
|
+
describe "when called by multiple tagger's" do
|
88
86
|
before(:each) do
|
89
|
-
@user_x =
|
90
|
-
@user_y =
|
91
|
-
@taggable = TaggableModel.create(:
|
92
|
-
|
93
|
-
@user_x.tag(@taggable, :
|
94
|
-
@user_y.tag(@taggable, :
|
95
|
-
|
96
|
-
@user_y.tag(@taggable, :
|
97
|
-
@user_y.tag(@taggable, :
|
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
|
101
|
-
@user_y.owned_tags.
|
97
|
+
|
98
|
+
it 'should delete owned tags' do
|
99
|
+
expect(@user_y.owned_tags).to be_empty
|
102
100
|
end
|
103
|
-
|
104
|
-
it
|
105
|
-
@user_x.owned_tags.
|
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
|
109
|
-
@taggable.all_tags_list_on(:tags).
|
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
|
+
|