acts-as-taggable-on 2.0.0 → 3.2.2
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +23 -0
- data/Appraisals +20 -0
- data/CHANGELOG.md +112 -0
- data/CONTRIBUTING.md +44 -0
- data/Gemfile +9 -5
- data/Guardfile +5 -0
- data/{MIT-LICENSE → LICENSE.md} +1 -1
- data/README.md +385 -0
- data/Rakefile +13 -53
- data/UPGRADING.md +13 -0
- data/acts-as-taggable-on.gemspec +38 -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/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_edge.gemfile +16 -0
- data/lib/acts-as-taggable-on.rb +65 -22
- data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +53 -24
- data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +120 -42
- data/lib/acts_as_taggable_on/acts_as_taggable_on/compatibility.rb +35 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +309 -124
- data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +36 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +96 -72
- data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +43 -36
- data/lib/acts_as_taggable_on/engine.rb +5 -0
- data/lib/acts_as_taggable_on/tag.rb +133 -63
- data/lib/acts_as_taggable_on/tag_list.rb +162 -81
- data/lib/acts_as_taggable_on/taggable.rb +101 -0
- data/lib/acts_as_taggable_on/{acts_as_tagger.rb → tagger.rb} +34 -13
- data/lib/acts_as_taggable_on/tagging.rb +28 -18
- data/lib/acts_as_taggable_on/tags_helper.rb +12 -14
- data/lib/acts_as_taggable_on/utils.rb +62 -0
- data/lib/acts_as_taggable_on/version.rb +4 -0
- data/lib/acts_as_taggable_on.rb +6 -0
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +189 -195
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +78 -81
- data/spec/acts_as_taggable_on/caching_spec.rb +76 -0
- data/spec/acts_as_taggable_on/related_spec.rb +89 -0
- data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +210 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +149 -61
- data/spec/acts_as_taggable_on/tag_spec.rb +245 -65
- data/spec/acts_as_taggable_on/taggable_spec.rb +776 -187
- data/spec/acts_as_taggable_on/tagger_spec.rb +119 -70
- data/spec/acts_as_taggable_on/tagging_spec.rb +17 -21
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +38 -22
- data/spec/acts_as_taggable_on/utils_spec.rb +15 -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/schema.rb +57 -18
- data/spec/spec_helper.rb +12 -47
- data/spec/support/array.rb +9 -0
- data/spec/support/database.rb +42 -0
- data/spec/support/database_cleaner.rb +21 -0
- metadata +286 -63
- data/CHANGELOG +0 -25
- data/README.rdoc +0 -221
- data/VERSION +0 -1
- data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +0 -7
- data/generators/acts_as_taggable_on_migration/templates/migration.rb +0 -29
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -53
- 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 -30
- /data/{spec/spec.opts → .rspec} +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Acts As Taggable On' do
|
|
4
|
+
|
|
5
|
+
describe 'Caching' do
|
|
6
|
+
before(:each) do
|
|
7
|
+
@taggable = CachedModel.new(name: 'Bob Jones')
|
|
8
|
+
@another_taggable = OtherCachedModel.new(name: 'John Smith')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should add saving of tag lists and cached tag lists to the instance' do
|
|
12
|
+
expect(@taggable).to respond_to(:save_cached_tag_list)
|
|
13
|
+
expect(@another_taggable).to respond_to(:save_cached_tag_list)
|
|
14
|
+
|
|
15
|
+
expect(@taggable).to respond_to(:save_tags)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'should add cached tag lists to the instance if cached column is not present' do
|
|
19
|
+
expect(TaggableModel.new(name: 'Art Kram')).to_not respond_to(:save_cached_tag_list)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'should generate a cached column checker for each tag type' do
|
|
23
|
+
expect(CachedModel).to respond_to(:caching_tag_list?)
|
|
24
|
+
expect(OtherCachedModel).to respond_to(:caching_language_list?)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'should not have cached tags' do
|
|
28
|
+
expect(@taggable.cached_tag_list).to be_blank
|
|
29
|
+
expect(@another_taggable.cached_language_list).to be_blank
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'should cache tags' do
|
|
33
|
+
@taggable.update_attributes(tag_list: 'awesome, epic')
|
|
34
|
+
expect(@taggable.cached_tag_list).to eq('awesome, epic')
|
|
35
|
+
|
|
36
|
+
@another_taggable.update_attributes(language_list: 'ruby, .net')
|
|
37
|
+
expect(@another_taggable.cached_language_list).to eq('ruby, .net')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should keep the cache' do
|
|
41
|
+
@taggable.update_attributes(tag_list: 'awesome, epic')
|
|
42
|
+
@taggable = CachedModel.find(@taggable.id)
|
|
43
|
+
@taggable.save!
|
|
44
|
+
expect(@taggable.cached_tag_list).to eq('awesome, epic')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'should update the cache' do
|
|
48
|
+
@taggable.update_attributes(tag_list: 'awesome, epic')
|
|
49
|
+
@taggable.update_attributes(tag_list: 'awesome')
|
|
50
|
+
expect(@taggable.cached_tag_list).to eq('awesome')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should remove the cache' do
|
|
54
|
+
@taggable.update_attributes(tag_list: 'awesome, epic')
|
|
55
|
+
@taggable.update_attributes(tag_list: '')
|
|
56
|
+
expect(@taggable.cached_tag_list).to be_blank
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'should have a tag list' do
|
|
60
|
+
@taggable.update_attributes(tag_list: 'awesome, epic')
|
|
61
|
+
@taggable = CachedModel.find(@taggable.id)
|
|
62
|
+
expect(@taggable.tag_list.sort).to eq(%w(awesome epic).sort)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'should keep the tag list' do
|
|
66
|
+
@taggable.update_attributes(tag_list: 'awesome, epic')
|
|
67
|
+
@taggable = CachedModel.find(@taggable.id)
|
|
68
|
+
@taggable.save!
|
|
69
|
+
expect(@taggable.tag_list.sort).to eq(%w(awesome epic).sort)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe 'CachingWithArray' do
|
|
74
|
+
pending '#TODO'
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Acts As Taggable On' do
|
|
4
|
+
|
|
5
|
+
describe 'Related Objects' do
|
|
6
|
+
|
|
7
|
+
#TODO, shared example
|
|
8
|
+
it 'should find related objects based on tag names on context' do
|
|
9
|
+
taggable1 = TaggableModel.create!(name: 'Taggable 1',tag_list: 'one, two')
|
|
10
|
+
taggable2 = TaggableModel.create!(name: 'Taggable 2',tag_list: 'three, four')
|
|
11
|
+
taggable3 = TaggableModel.create!(name: 'Taggable 3',tag_list: 'one, four')
|
|
12
|
+
|
|
13
|
+
expect(taggable1.find_related_tags).to include(taggable3)
|
|
14
|
+
expect(taggable1.find_related_tags).to_not include(taggable2)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'finds related tags for ordered taggable on' do
|
|
18
|
+
taggable1 = OrderedTaggableModel.create!(name: 'Taggable 1',colour_list: 'one, two')
|
|
19
|
+
taggable2 = OrderedTaggableModel.create!(name: 'Taggable 2',colour_list: 'three, four')
|
|
20
|
+
taggable3 = OrderedTaggableModel.create!(name: 'Taggable 3',colour_list: 'one, four')
|
|
21
|
+
|
|
22
|
+
expect(taggable1.find_related_colours).to include(taggable3)
|
|
23
|
+
expect(taggable1.find_related_colours).to_not include(taggable2)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'should find related objects based on tag names on context - non standard id' do
|
|
27
|
+
taggable1 = NonStandardIdTaggableModel.create!(name: 'Taggable 1',tag_list: 'one, two')
|
|
28
|
+
taggable2 = NonStandardIdTaggableModel.create!(name: 'Taggable 2',tag_list: 'three, four')
|
|
29
|
+
taggable3 = NonStandardIdTaggableModel.create!(name: 'Taggable 3',tag_list: 'one, four')
|
|
30
|
+
|
|
31
|
+
expect(taggable1.find_related_tags).to include(taggable3)
|
|
32
|
+
expect(taggable1.find_related_tags).to_not include(taggable2)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'should find other related objects based on tag names on context' do
|
|
36
|
+
taggable1 = TaggableModel.create!(name: 'Taggable 1',tag_list: 'one, two')
|
|
37
|
+
taggable2 = OtherTaggableModel.create!(name: 'Taggable 2',tag_list: 'three, four')
|
|
38
|
+
taggable3 = OtherTaggableModel.create!(name: 'Taggable 3',tag_list: 'one, four')
|
|
39
|
+
|
|
40
|
+
expect(taggable1.find_related_tags_for(OtherTaggableModel)).to include(taggable3)
|
|
41
|
+
expect(taggable1.find_related_tags_for(OtherTaggableModel)).to_not include(taggable2)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
shared_examples "a collection" do
|
|
46
|
+
it do
|
|
47
|
+
taggable1 = described_class.create!(name: 'Taggable 1', tag_list: 'one')
|
|
48
|
+
taggable2 = described_class.create!(name: 'Taggable 2', tag_list: 'one, two')
|
|
49
|
+
|
|
50
|
+
expect(taggable1.find_related_tags).to include(taggable2)
|
|
51
|
+
expect(taggable1.find_related_tags).to_not include(taggable1)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# it 'should not include the object itself in the list of related objects' do
|
|
56
|
+
describe TaggableModel do
|
|
57
|
+
it_behaves_like "a collection"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# it 'should not include the object itself in the list of related objects - non standard id' do
|
|
61
|
+
describe NonStandardIdTaggableModel do
|
|
62
|
+
it_behaves_like "a collection"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context 'Ignored Tags' do
|
|
66
|
+
let(:taggable1) { TaggableModel.create!(name: 'Taggable 1', tag_list: 'one, two, four') }
|
|
67
|
+
let(:taggable2) { TaggableModel.create!(name: 'Taggable 2', tag_list: 'two, three') }
|
|
68
|
+
let(:taggable3) { TaggableModel.create!(name: 'Taggable 3', tag_list: 'one, three') }
|
|
69
|
+
|
|
70
|
+
it 'should not include ignored tags in related search' do
|
|
71
|
+
expect(taggable1.find_related_tags(ignore: 'two')).to_not include(taggable2)
|
|
72
|
+
expect(taggable1.find_related_tags(ignore: 'two')).to include(taggable3)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'should accept array of ignored tags' do
|
|
76
|
+
taggable4 = TaggableModel.create!(name: 'Taggable 4', tag_list: 'four')
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
expect(taggable1.find_related_tags(ignore: ['two', 'four'])).to_not include(taggable2)
|
|
80
|
+
expect(taggable1.find_related_tags(ignore: ['two', 'four'])).to_not include(taggable4)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'should accept symbols as ignored tags' do
|
|
84
|
+
expect(taggable1.find_related_tags(ignore: :two)).to_not include(taggable2)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Single Table Inheritance' do
|
|
4
|
+
let(:taggable) { TaggableModel.new(name: 'taggable model') }
|
|
5
|
+
|
|
6
|
+
let(:inheriting_model) { InheritingTaggableModel.new(name: 'Inheriting Taggable Model') }
|
|
7
|
+
let(:altered_inheriting) { AlteredInheritingTaggableModel.new(name: 'Altered Inheriting Model') }
|
|
8
|
+
|
|
9
|
+
1.upto(4) do |n|
|
|
10
|
+
let(:"inheriting_#{n}") { InheritingTaggableModel.new(name: "Inheriting Model #{n}") }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
let(:student) { Student.create! }
|
|
14
|
+
|
|
15
|
+
describe 'tag contexts' do
|
|
16
|
+
it 'should pass on to STI-inherited models' do
|
|
17
|
+
expect(inheriting_model).to respond_to(:tag_list, :skill_list, :language_list)
|
|
18
|
+
expect(altered_inheriting).to respond_to(:tag_list, :skill_list, :language_list)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should pass on to altered STI models' do
|
|
22
|
+
expect(altered_inheriting).to respond_to(:part_list)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context 'matching contexts' do
|
|
27
|
+
|
|
28
|
+
before do
|
|
29
|
+
inheriting_1.offering_list = 'one, two'
|
|
30
|
+
inheriting_1.need_list = 'one, two'
|
|
31
|
+
inheriting_1.save!
|
|
32
|
+
|
|
33
|
+
inheriting_2.need_list = 'one, two'
|
|
34
|
+
inheriting_2.save!
|
|
35
|
+
|
|
36
|
+
inheriting_3.offering_list = 'one, two'
|
|
37
|
+
inheriting_3.save!
|
|
38
|
+
|
|
39
|
+
inheriting_4.tag_list = 'one, two, three, four'
|
|
40
|
+
inheriting_4.save!
|
|
41
|
+
|
|
42
|
+
taggable.need_list = 'one, two'
|
|
43
|
+
taggable.save!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'should find objects with tags of matching contexts' do
|
|
47
|
+
expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to include(inheriting_2)
|
|
48
|
+
expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_3)
|
|
49
|
+
expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_4)
|
|
50
|
+
expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(taggable)
|
|
51
|
+
|
|
52
|
+
expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(inheriting_2)
|
|
53
|
+
expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_3)
|
|
54
|
+
expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_4)
|
|
55
|
+
expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to include(taggable)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'should not include the object itself in the list of related objects with tags of matching contexts' do
|
|
59
|
+
expect(inheriting_1.find_matching_contexts(:offerings, :needs)).to_not include(inheriting_1)
|
|
60
|
+
expect(inheriting_1.find_matching_contexts_for(InheritingTaggableModel, :offerings, :needs)).to_not include(inheriting_1)
|
|
61
|
+
expect(inheriting_1.find_matching_contexts_for(TaggableModel, :offerings, :needs)).to_not include(inheriting_1)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context 'find related tags' do
|
|
66
|
+
before do
|
|
67
|
+
inheriting_1.tag_list = 'one, two'
|
|
68
|
+
inheriting_1.save
|
|
69
|
+
|
|
70
|
+
inheriting_2.tag_list = 'three, four'
|
|
71
|
+
inheriting_2.save
|
|
72
|
+
|
|
73
|
+
inheriting_3.tag_list = 'one, four'
|
|
74
|
+
inheriting_3.save
|
|
75
|
+
|
|
76
|
+
taggable.tag_list = 'one, two, three, four'
|
|
77
|
+
taggable.save
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'should find related objects based on tag names on context' do
|
|
81
|
+
expect(inheriting_1.find_related_tags).to include(inheriting_3)
|
|
82
|
+
expect(inheriting_1.find_related_tags).to_not include(inheriting_2)
|
|
83
|
+
expect(inheriting_1.find_related_tags).to_not include(taggable)
|
|
84
|
+
|
|
85
|
+
expect(inheriting_1.find_related_tags_for(TaggableModel)).to include(inheriting_3)
|
|
86
|
+
expect(inheriting_1.find_related_tags_for(TaggableModel)).to_not include(inheriting_2)
|
|
87
|
+
expect(inheriting_1.find_related_tags_for(TaggableModel)).to include(taggable)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'should not include the object itself in the list of related objects' do
|
|
91
|
+
expect(inheriting_1.find_related_tags).to_not include(inheriting_1)
|
|
92
|
+
expect(inheriting_1.find_related_tags_for(InheritingTaggableModel)).to_not include(inheriting_1)
|
|
93
|
+
expect(inheriting_1.find_related_tags_for(TaggableModel)).to_not include(inheriting_1)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe 'tag list' do
|
|
98
|
+
before do
|
|
99
|
+
@inherited_same = InheritingTaggableModel.new(name: 'inherited same')
|
|
100
|
+
@inherited_different = AlteredInheritingTaggableModel.new(name: 'inherited different')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#TODO, shared example
|
|
104
|
+
it 'should be able to save tags for inherited models' do
|
|
105
|
+
inheriting_model.tag_list = 'bob, kelso'
|
|
106
|
+
inheriting_model.save
|
|
107
|
+
expect(InheritingTaggableModel.tagged_with('bob').first).to eq(inheriting_model)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'should find STI tagged models on the superclass' do
|
|
111
|
+
inheriting_model.tag_list = 'bob, kelso'
|
|
112
|
+
inheriting_model.save
|
|
113
|
+
expect(TaggableModel.tagged_with('bob').first).to eq(inheriting_model)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'should be able to add on contexts only to some subclasses' do
|
|
117
|
+
altered_inheriting.part_list = 'fork, spoon'
|
|
118
|
+
altered_inheriting.save
|
|
119
|
+
expect(InheritingTaggableModel.tagged_with('fork', on: :parts)).to be_empty
|
|
120
|
+
expect(AlteredInheritingTaggableModel.tagged_with('fork', on: :parts).first).to eq(altered_inheriting)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'should have different tag_counts_on for inherited models' do
|
|
124
|
+
inheriting_model.tag_list = 'bob, kelso'
|
|
125
|
+
inheriting_model.save!
|
|
126
|
+
altered_inheriting.tag_list = 'fork, spoon'
|
|
127
|
+
altered_inheriting.save!
|
|
128
|
+
|
|
129
|
+
expect(InheritingTaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso))
|
|
130
|
+
expect(AlteredInheritingTaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(fork spoon))
|
|
131
|
+
expect(TaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso fork spoon))
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'should have different tags_on for inherited models' do
|
|
135
|
+
inheriting_model.tag_list = 'bob, kelso'
|
|
136
|
+
inheriting_model.save!
|
|
137
|
+
altered_inheriting.tag_list = 'fork, spoon'
|
|
138
|
+
altered_inheriting.save!
|
|
139
|
+
|
|
140
|
+
expect(InheritingTaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso))
|
|
141
|
+
expect(AlteredInheritingTaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(fork spoon))
|
|
142
|
+
expect(TaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso fork spoon))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'should store same tag without validation conflict' do
|
|
146
|
+
taggable.tag_list = 'one'
|
|
147
|
+
taggable.save!
|
|
148
|
+
|
|
149
|
+
inheriting_model.tag_list = 'one'
|
|
150
|
+
inheriting_model.save!
|
|
151
|
+
|
|
152
|
+
inheriting_model.update_attributes! name: 'foo'
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
describe 'ownership' do
|
|
157
|
+
it 'should have taggings' do
|
|
158
|
+
student.tag(taggable, with: 'ruby,scheme', on: :tags)
|
|
159
|
+
expect(student.owned_taggings.count).to eq(2)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'should have tags' do
|
|
163
|
+
student.tag(taggable, with: 'ruby,scheme', on: :tags)
|
|
164
|
+
expect(student.owned_tags.count).to eq(2)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'should return tags for the inheriting tagger' do
|
|
168
|
+
student.tag(taggable, with: 'ruby, scheme', on: :tags)
|
|
169
|
+
expect(taggable.tags_from(student)).to eq(%w(ruby scheme))
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'returns owner tags on the tagger' do
|
|
173
|
+
student.tag(taggable, with: 'ruby, scheme', on: :tags)
|
|
174
|
+
expect(taggable.owner_tags_on(student, :tags).count).to eq(2)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it 'should scope objects returned by tagged_with by owners' do
|
|
178
|
+
student.tag(taggable, with: 'ruby, scheme', on: :tags)
|
|
179
|
+
expect(TaggableModel.tagged_with(%w(ruby scheme), owned_by: student).count).to eq(1)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
describe 'a subclass of Tag' do
|
|
184
|
+
let(:company) { Company.new(:name => 'Dewey, Cheatham & Howe') }
|
|
185
|
+
let(:user) { User.create! }
|
|
186
|
+
|
|
187
|
+
subject { Market.create! :name => 'finance' }
|
|
188
|
+
|
|
189
|
+
its(:type) { should eql 'Market' }
|
|
190
|
+
|
|
191
|
+
it 'sets STI type through string list' do
|
|
192
|
+
company.market_list = 'law, accounting'
|
|
193
|
+
company.save!
|
|
194
|
+
expect(Market.count).to eq(2)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it 'does not interfere with a normal Tag context on the same model' do
|
|
198
|
+
company.location_list = 'cambridge'
|
|
199
|
+
company.save!
|
|
200
|
+
expect(ActsAsTaggableOn::Tag.where(name: 'cambridge', type: nil)).to_not be_empty
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'is returned with proper type through ownership' do
|
|
204
|
+
user.tag(company, :with => 'ripoffs, rackets', :on => :markets)
|
|
205
|
+
tags = company.owner_tags_on(user, :markets)
|
|
206
|
+
expect(tags.all? { |tag| tag.is_a? Market }).to be_truthy
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
@@ -1,70 +1,158 @@
|
|
|
1
|
-
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'spec_helper'
|
|
2
3
|
|
|
3
|
-
describe TagList do
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
4
|
+
describe ActsAsTaggableOn::TagList do
|
|
5
|
+
let(:tag_list) { ActsAsTaggableOn::TagList.new('awesome', 'radical') }
|
|
6
|
+
let(:another_tag_list) { ActsAsTaggableOn::TagList.new('awesome','crazy', 'alien') }
|
|
7
|
+
|
|
8
|
+
it { should be_kind_of Array }
|
|
9
|
+
|
|
10
|
+
it '#from should return empty array if empty array is passed' do
|
|
11
|
+
expect(ActsAsTaggableOn::TagList.from([])).to be_empty
|
|
33
12
|
end
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
13
|
+
|
|
14
|
+
describe '#add' do
|
|
15
|
+
it 'should be able to be add a new tag word' do
|
|
16
|
+
tag_list.add('cool')
|
|
17
|
+
expect(tag_list.include?('cool')).to be_truthy
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should be able to add delimited lists of words' do
|
|
21
|
+
tag_list.add('cool, wicked', parse: true)
|
|
22
|
+
expect(tag_list).to include('cool', 'wicked')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should be able to add delimited list of words with quoted delimiters' do
|
|
26
|
+
tag_list.add("'cool, wicked', \"really cool, really wicked\"", parse: true)
|
|
27
|
+
expect(tag_list).to include('cool, wicked', 'really cool, really wicked')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should be able to handle other uses of quotation marks correctly' do
|
|
31
|
+
tag_list.add("john's cool car, mary's wicked toy", parse: true)
|
|
32
|
+
expect(tag_list).to include("john's cool car", "mary's wicked toy")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'should be able to add an array of words' do
|
|
36
|
+
tag_list.add(%w(cool wicked), parse: true)
|
|
37
|
+
expect(tag_list).to include('cool', 'wicked')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should quote escape tags with commas in them' do
|
|
41
|
+
tag_list.add('cool', 'rad,bodacious')
|
|
42
|
+
expect(tag_list.to_s).to eq("awesome, radical, cool, \"rad,bodacious\"")
|
|
43
|
+
end
|
|
44
|
+
|
|
39
45
|
end
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
|
|
47
|
+
describe '#remove' do
|
|
48
|
+
it 'should be able to remove words' do
|
|
49
|
+
tag_list.remove('awesome')
|
|
50
|
+
expect(tag_list).to_not include('awesome')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should be able to remove delimited lists of words' do
|
|
54
|
+
tag_list.remove('awesome, radical', parse: true)
|
|
55
|
+
expect(tag_list).to be_empty
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'should be able to remove an array of words' do
|
|
59
|
+
tag_list.remove(%w(awesome radical), parse: true)
|
|
60
|
+
expect(tag_list).to be_empty
|
|
61
|
+
end
|
|
44
62
|
end
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
|
|
64
|
+
describe '#+' do
|
|
65
|
+
it 'should not have duplicate tags' do
|
|
66
|
+
new_tag_list = tag_list + another_tag_list
|
|
67
|
+
expect(tag_list).to eq(%w[awesome radical])
|
|
68
|
+
expect(another_tag_list).to eq(%w[awesome crazy alien])
|
|
69
|
+
expect(new_tag_list).to eq(%w[awesome radical crazy alien])
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'should have class : ActsAsTaggableOn::TagList' do
|
|
73
|
+
new_tag_list = tag_list + another_tag_list
|
|
74
|
+
expect(new_tag_list.class).to eq(ActsAsTaggableOn::TagList)
|
|
75
|
+
end
|
|
49
76
|
end
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
77
|
+
|
|
78
|
+
describe '#concat' do
|
|
79
|
+
it 'should not have duplicate tags' do
|
|
80
|
+
expect(tag_list.concat(another_tag_list)).to eq(%w[awesome radical crazy alien])
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'should have class : ActsAsTaggableOn::TagList' do
|
|
84
|
+
new_tag_list = tag_list.concat(another_tag_list)
|
|
85
|
+
expect(new_tag_list.class).to eq(ActsAsTaggableOn::TagList)
|
|
86
|
+
end
|
|
54
87
|
end
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
88
|
+
|
|
89
|
+
describe '#to_s' do
|
|
90
|
+
it 'should give a delimited list of words when converted to string' do
|
|
91
|
+
expect(tag_list.to_s).to eq('awesome, radical')
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'should be able to call to_s on a frozen tag list' do
|
|
95
|
+
tag_list.freeze
|
|
96
|
+
expect(-> { tag_list.add('cool', 'rad,bodacious') }).to raise_error
|
|
97
|
+
expect(-> { tag_list.to_s }).to_not raise_error
|
|
98
|
+
end
|
|
58
99
|
end
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
100
|
+
|
|
101
|
+
describe 'cleaning' do
|
|
102
|
+
it 'should parameterize if force_parameterize is set to true' do
|
|
103
|
+
ActsAsTaggableOn.force_parameterize = true
|
|
104
|
+
tag_list = ActsAsTaggableOn::TagList.new('awesome()', 'radical)(cc')
|
|
105
|
+
|
|
106
|
+
expect(tag_list.to_s).to eq('awesome, radical-cc')
|
|
107
|
+
ActsAsTaggableOn.force_parameterize = false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'should lowercase if force_lowercase is set to true' do
|
|
111
|
+
ActsAsTaggableOn.force_lowercase = true
|
|
112
|
+
|
|
113
|
+
tag_list = ActsAsTaggableOn::TagList.new('aweSomE', 'RaDicaL', 'Entrée')
|
|
114
|
+
expect(tag_list.to_s).to eq('awesome, radical, entrée')
|
|
115
|
+
|
|
116
|
+
ActsAsTaggableOn.force_lowercase = false
|
|
117
|
+
end
|
|
118
|
+
|
|
63
119
|
end
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
120
|
+
|
|
121
|
+
describe 'Multiple Delimiter' do
|
|
122
|
+
before do
|
|
123
|
+
@old_delimiter = ActsAsTaggableOn.delimiter
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
after do
|
|
127
|
+
ActsAsTaggableOn.delimiter = @old_delimiter
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'should separate tags by delimiters' do
|
|
131
|
+
ActsAsTaggableOn.delimiter = [',', ' ', '\|']
|
|
132
|
+
tag_list = ActsAsTaggableOn::TagList.from 'cool, data|I have'
|
|
133
|
+
expect(tag_list.to_s).to eq('cool, data, I, have')
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'should escape quote' do
|
|
137
|
+
ActsAsTaggableOn.delimiter = [',', ' ', '\|']
|
|
138
|
+
tag_list = ActsAsTaggableOn::TagList.from "'I have'|cool, data"
|
|
139
|
+
expect(tag_list.to_s).to eq('"I have", cool, data')
|
|
140
|
+
|
|
141
|
+
tag_list = ActsAsTaggableOn::TagList.from '"I, have"|cool, data'
|
|
142
|
+
expect(tag_list.to_s).to eq('"I, have", cool, data')
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'should work for utf8 delimiter and long delimiter' do
|
|
146
|
+
ActsAsTaggableOn.delimiter = [',', '的', '可能是']
|
|
147
|
+
tag_list = ActsAsTaggableOn::TagList.from '我的东西可能是不见了,还好有备份'
|
|
148
|
+
expect(tag_list.to_s).to eq('我, 东西, 不见了, 还好有备份')
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'should work for multiple quoted tags' do
|
|
152
|
+
ActsAsTaggableOn.delimiter = [',']
|
|
153
|
+
tag_list = ActsAsTaggableOn::TagList.from '"Ruby Monsters","eat Katzenzungen"'
|
|
154
|
+
expect(tag_list.to_s).to eq('Ruby Monsters, eat Katzenzungen')
|
|
155
|
+
end
|
|
69
156
|
end
|
|
70
|
-
|
|
157
|
+
|
|
158
|
+
end
|