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
@@ -0,0 +1,46 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe ActsAsTaggableOn::TagListParser do
5
+ it '#parse should return empty array if empty array is passed' do
6
+ expect(ActsAsTaggableOn::TagListParser.parse([])).to be_empty
7
+ end
8
+
9
+ describe 'Multiple Delimiter' do
10
+ before do
11
+ @old_delimiter = ActsAsTaggableOn.delimiter
12
+ end
13
+
14
+ after do
15
+ ActsAsTaggableOn.delimiter = @old_delimiter
16
+ end
17
+
18
+ it 'should separate tags by delimiters' do
19
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
20
+ tag_list = ActsAsTaggableOn::TagListParser.parse('cool, data|I have')
21
+ expect(tag_list.to_s).to eq('cool, data, I, have')
22
+ end
23
+
24
+ it 'should escape quote' do
25
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
26
+ tag_list = ActsAsTaggableOn::TagListParser.parse "'I have'|cool, data"
27
+ expect(tag_list.to_s).to eq('"I have", cool, data')
28
+
29
+ tag_list = ActsAsTaggableOn::TagListParser.parse '"I, have"|cool, data'
30
+ expect(tag_list.to_s).to eq('"I, have", cool, data')
31
+ end
32
+
33
+ it 'should work for utf8 delimiter and long delimiter' do
34
+ ActsAsTaggableOn.delimiter = [',', '的', '可能是']
35
+ tag_list = ActsAsTaggableOn::TagListParser.parse('我的东西可能是不见了,还好有备份')
36
+ expect(tag_list.to_s).to eq('我, 东西, 不见了, 还好有备份')
37
+ end
38
+
39
+ it 'should work for multiple quoted tags' do
40
+ ActsAsTaggableOn.delimiter = [',']
41
+ tag_list = ActsAsTaggableOn::TagListParser.parse('"Ruby Monsters","eat Katzenzungen"')
42
+ expect(tag_list.to_s).to eq('Ruby Monsters, eat Katzenzungen')
43
+ end
44
+ end
45
+
46
+ end
@@ -1,70 +1,150 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+
5
+ describe ActsAsTaggableOn::TagList do
6
+ let(:tag_list) { ActsAsTaggableOn::TagList.new('awesome', 'radical') }
7
+ let(:another_tag_list) { ActsAsTaggableOn::TagList.new('awesome','crazy', 'alien') }
8
+
9
+ it { should be_kind_of Array }
10
+
11
+
12
+
13
+ describe '#add' do
14
+ it 'should be able to be add a new tag word' do
15
+ tag_list.add('cool')
16
+ expect(tag_list.include?('cool')).to be_truthy
17
+ end
18
+
19
+ it 'should be able to add delimited lists of words' do
20
+ tag_list.add('cool, wicked', parse: true)
21
+ expect(tag_list).to include('cool', 'wicked')
22
+ end
23
+
24
+ it 'should be able to add delimited list of words with quoted delimiters' do
25
+ tag_list.add("'cool, wicked', \"really cool, really wicked\"", parse: true)
26
+ expect(tag_list).to include('cool, wicked', 'really cool, really wicked')
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
+ expect(tag_list).to include("john's cool car", "mary's wicked toy")
32
+ end
33
+
34
+ it 'should be able to add an array of words' do
35
+ tag_list.add(%w(cool wicked), parse: true)
36
+ expect(tag_list).to include('cool', 'wicked')
37
+ end
38
+
39
+ it 'should quote escape tags with commas in them' do
40
+ tag_list.add('cool', 'rad,bodacious')
41
+ expect(tag_list.to_s).to eq("awesome, radical, cool, \"rad,bodacious\"")
42
+ end
2
43
 
3
- describe TagList do
4
- before(:each) do
5
- @tag_list = TagList.new("awesome","radical")
6
- end
7
-
8
- it "should be an array" do
9
- @tag_list.is_a?(Array).should be_true
10
- end
11
-
12
- it "should be able to be add a new tag word" do
13
- @tag_list.add("cool")
14
- @tag_list.include?("cool").should be_true
15
- end
16
-
17
- it "should be able to add delimited lists of words" do
18
- @tag_list.add("cool, wicked", :parse => true)
19
- @tag_list.include?("cool").should be_true
20
- @tag_list.include?("wicked").should be_true
21
- end
22
-
23
- it "should be able to add delimited list of words with quoted delimiters" do
24
- @tag_list.add("'cool, wicked', \"really cool, really wicked\"", :parse => true)
25
- @tag_list.include?("cool, wicked").should be_true
26
- @tag_list.include?("really cool, really wicked").should be_true
27
- end
28
-
29
- it "should be able to handle other uses of quotation marks correctly" do
30
- @tag_list.add("john's cool car, mary's wicked toy", :parse => true)
31
- @tag_list.include?("john's cool car").should be_true
32
- @tag_list.include?("mary's wicked toy").should be_true
33
- end
34
-
35
- it "should be able to add an array of words" do
36
- @tag_list.add(["cool", "wicked"], :parse => true)
37
- @tag_list.include?("cool").should be_true
38
- @tag_list.include?("wicked").should be_true
39
44
  end
40
-
41
- it "should be able to remove words" do
42
- @tag_list.remove("awesome")
43
- @tag_list.include?("awesome").should be_false
45
+
46
+ describe '#remove' do
47
+ it 'should be able to remove words' do
48
+ tag_list.remove('awesome')
49
+ expect(tag_list).to_not include('awesome')
50
+ end
51
+
52
+ it 'should be able to remove delimited lists of words' do
53
+ tag_list.remove('awesome, radical', parse: true)
54
+ expect(tag_list).to be_empty
55
+ end
56
+
57
+ it 'should be able to remove an array of words' do
58
+ tag_list.remove(%w(awesome radical), parse: true)
59
+ expect(tag_list).to be_empty
60
+ end
44
61
  end
45
-
46
- it "should be able to remove delimited lists of words" do
47
- @tag_list.remove("awesome, radical", :parse => true)
48
- @tag_list.should be_empty
62
+
63
+ describe '#+' do
64
+ it 'should not have duplicate tags' do
65
+ new_tag_list = tag_list + another_tag_list
66
+ expect(tag_list).to eq(%w[awesome radical])
67
+ expect(another_tag_list).to eq(%w[awesome crazy alien])
68
+ expect(new_tag_list).to eq(%w[awesome radical crazy alien])
69
+ end
70
+
71
+ it 'should have class : ActsAsTaggableOn::TagList' do
72
+ new_tag_list = tag_list + another_tag_list
73
+ expect(new_tag_list.class).to eq(ActsAsTaggableOn::TagList)
74
+ end
49
75
  end
50
-
51
- it "should be able to remove an array of words" do
52
- @tag_list.remove(["awesome", "radical"], :parse => true)
53
- @tag_list.should be_empty
76
+
77
+ describe '#concat' do
78
+ it 'should not have duplicate tags' do
79
+ expect(tag_list.concat(another_tag_list)).to eq(%w[awesome radical crazy alien])
80
+ end
81
+
82
+ it 'should have class : ActsAsTaggableOn::TagList' do
83
+ new_tag_list = tag_list.concat(another_tag_list)
84
+ expect(new_tag_list.class).to eq(ActsAsTaggableOn::TagList)
85
+ end
54
86
  end
55
-
56
- it "should give a delimited list of words when converted to string" do
57
- @tag_list.to_s.should == "awesome, radical"
87
+
88
+ describe '#to_s' do
89
+ it 'should give a delimited list of words when converted to string' do
90
+ expect(tag_list.to_s).to eq('awesome, radical')
91
+ end
92
+
93
+ it 'should be able to call to_s on a frozen tag list' do
94
+ tag_list.freeze
95
+ expect(-> { tag_list.add('cool', 'rad,bodacious') }).to raise_error
96
+ expect(-> { tag_list.to_s }).to_not raise_error
97
+ end
58
98
  end
59
-
60
- it "should quote escape tags with commas in them" do
61
- @tag_list.add("cool","rad,bodacious")
62
- @tag_list.to_s.should == "awesome, radical, cool, \"rad,bodacious\""
99
+
100
+ describe 'cleaning' do
101
+ it 'should parameterize if force_parameterize is set to true' do
102
+ ActsAsTaggableOn.force_parameterize = true
103
+ tag_list = ActsAsTaggableOn::TagList.new('awesome()', 'radical)(cc')
104
+
105
+ expect(tag_list.to_s).to eq('awesome, radical-cc')
106
+ ActsAsTaggableOn.force_parameterize = false
107
+ end
108
+
109
+ it 'should lowercase if force_lowercase is set to true' do
110
+ ActsAsTaggableOn.force_lowercase = true
111
+
112
+ tag_list = ActsAsTaggableOn::TagList.new('aweSomE', 'RaDicaL', 'Entrée')
113
+ expect(tag_list.to_s).to eq('awesome, radical, entrée')
114
+
115
+ ActsAsTaggableOn.force_lowercase = false
116
+ end
63
117
  end
64
-
65
- it "should be able to call to_s on a frozen tag list" do
66
- @tag_list.freeze
67
- lambda { @tag_list.add("cool","rad,bodacious") }.should raise_error
68
- lambda { @tag_list.to_s }.should_not raise_error
118
+
119
+ describe 'custom parser' do
120
+ let(:parser) { double(parse: %w(cool wicked)) }
121
+ let(:parser_class) { stub_const('MyParser', Class) }
122
+
123
+ it 'should use a the default parser if none is set as parameter' do
124
+ allow(ActsAsTaggableOn.default_parser).to receive(:new).and_return(parser)
125
+ ActsAsTaggableOn::TagList.new('cool, wicked', parse: true)
126
+
127
+ expect(parser).to have_received(:parse)
128
+ end
129
+
130
+ it 'should use the custom parser passed as parameter' do
131
+ allow(parser_class).to receive(:new).and_return(parser)
132
+
133
+ ActsAsTaggableOn::TagList.new('cool, wicked', parser: parser_class)
134
+
135
+ expect(parser).to have_received(:parse)
136
+ end
137
+
138
+ it 'should use the parser setted as attribute' do
139
+ allow(parser_class).to receive(:new).with('new, tag').and_return(parser)
140
+
141
+ tag_list = ActsAsTaggableOn::TagList.new('example')
142
+ tag_list.parser = parser_class
143
+ tag_list.add('new, tag', parse: true)
144
+
145
+ expect(parser).to have_received(:parse)
146
+ end
69
147
  end
70
- end
148
+
149
+
150
+ end
@@ -1,115 +1,325 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'db/migrate/2_add_missing_unique_indices.rb'
2
4
 
3
- describe Tag do
5
+
6
+ shared_examples_for 'without unique index' do
7
+ prepend_before(:all) { AddMissingUniqueIndices.down }
8
+ append_after(:all) do
9
+ ActsAsTaggableOn::Tag.delete_all
10
+ AddMissingUniqueIndices.up
11
+ end
12
+ end
13
+
14
+ describe ActsAsTaggableOn::Tag do
4
15
  before(:each) do
5
- clean_database!
6
- @tag = Tag.new
7
- @user = TaggableModel.create(:name => "Pablo")
16
+ @tag = ActsAsTaggableOn::Tag.new
17
+ @user = TaggableModel.create(name: 'Pablo')
18
+ end
19
+
20
+
21
+ describe 'named like any' do
22
+ context 'case insensitive collation and unique index on tag name', if: using_case_insensitive_collation? do
23
+ before(:each) do
24
+ ActsAsTaggableOn::Tag.create(name: 'Awesome')
25
+ ActsAsTaggableOn::Tag.create(name: 'epic')
26
+ end
27
+
28
+ it 'should find both tags' do
29
+ expect(ActsAsTaggableOn::Tag.named_like_any(%w(awesome epic)).count).to eq(2)
30
+ end
31
+ end
32
+
33
+ context 'case insensitive collation without indexes or case sensitive collation with indexes' do
34
+ if using_case_insensitive_collation?
35
+ include_context 'without unique index'
36
+ end
37
+
38
+ before(:each) do
39
+ ActsAsTaggableOn::Tag.create(name: 'Awesome')
40
+ ActsAsTaggableOn::Tag.create(name: 'awesome')
41
+ ActsAsTaggableOn::Tag.create(name: 'epic')
42
+ end
43
+
44
+ it 'should find both tags' do
45
+ expect(ActsAsTaggableOn::Tag.named_like_any(%w(awesome epic)).count).to eq(3)
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'named any' do
51
+ context 'with some special characters combinations', if: using_mysql? do
52
+ it 'should not raise an invalid encoding exception' do
53
+ expect{ActsAsTaggableOn::Tag.named_any(["holä", "hol'ä"])}.not_to raise_error
54
+ end
55
+ end
8
56
  end
9
57
 
10
- describe "named like any" do
58
+ describe 'find or create by name' do
11
59
  before(:each) do
12
- Tag.create(:name => "awesome")
13
- Tag.create(:name => "epic")
60
+ @tag.name = 'awesome'
61
+ @tag.save
62
+ end
63
+
64
+ it 'should find by name' do
65
+ expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('awesome')).to eq(@tag)
66
+ end
67
+
68
+ it 'should find by name case insensitive' do
69
+ expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('AWESOME')).to eq(@tag)
14
70
  end
15
71
 
16
- it "should find both tags" do
17
- Tag.named_like_any(["awesome", "epic"]).should have(2).items
72
+ it 'should create by name' do
73
+ expect(-> {
74
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('epic')
75
+ }).to change(ActsAsTaggableOn::Tag, :count).by(1)
18
76
  end
19
77
  end
20
78
 
21
- describe "find or create by name" do
79
+ describe 'find or create by unicode name', unless: using_sqlite? do
22
80
  before(:each) do
23
- @tag.name = "awesome"
81
+ @tag.name = 'привет'
24
82
  @tag.save
25
83
  end
26
84
 
27
- it "should find by name" do
28
- Tag.find_or_create_with_like_by_name("awesome").should == @tag
85
+ it 'should find by name' do
86
+ expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('привет')).to eq(@tag)
29
87
  end
30
88
 
31
- it "should find by name case insensitive" do
32
- Tag.find_or_create_with_like_by_name("AWESOME").should == @tag
89
+ it 'should find by name case insensitive' do
90
+ expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('ПРИВЕТ')).to eq(@tag)
33
91
  end
34
92
 
35
- it "should create by name" do
36
- lambda {
37
- Tag.find_or_create_with_like_by_name("epic")
38
- }.should change(Tag, :count).by(1)
93
+ it 'should find by name accent insensitive', if: using_case_insensitive_collation? do
94
+ @tag.name = 'inupiat'
95
+ @tag.save
96
+ expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('Iñupiat')).to eq(@tag)
39
97
  end
40
98
  end
41
99
 
42
- describe "find or create all by any name" do
100
+ describe 'find or create all by any name' do
43
101
  before(:each) do
44
- @tag.name = "awesome"
102
+ @tag.name = 'awesome'
45
103
  @tag.save
46
104
  end
47
105
 
48
- it "should find by name" do
49
- Tag.find_or_create_all_with_like_by_name("awesome").should == [@tag]
106
+ it 'should find by name' do
107
+ expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('awesome')).to eq([@tag])
108
+ end
109
+
110
+ it 'should find by name case insensitive' do
111
+ expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('AWESOME')).to eq([@tag])
112
+ end
113
+
114
+ context 'case sensitive' do
115
+ if using_case_insensitive_collation?
116
+ include_context 'without unique index'
117
+ end
118
+
119
+ it 'should find by name case sensitive' do
120
+ ActsAsTaggableOn.strict_case_match = true
121
+ expect {
122
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('AWESOME')
123
+ }.to change(ActsAsTaggableOn::Tag, :count).by(1)
124
+ end
50
125
  end
51
126
 
52
- it "should find by name case insensitive" do
53
- Tag.find_or_create_all_with_like_by_name("AWESOME").should == [@tag]
127
+ it 'should create by name' do
128
+ expect {
129
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('epic')
130
+ }.to change(ActsAsTaggableOn::Tag, :count).by(1)
54
131
  end
55
132
 
56
- it "should create by name" do
57
- lambda {
58
- Tag.find_or_create_all_with_like_by_name("epic")
59
- }.should change(Tag, :count).by(1)
133
+ context 'case sensitive' do
134
+ if using_case_insensitive_collation?
135
+ include_context 'without unique index'
136
+ end
137
+
138
+ it 'should find or create by name case sensitive' do
139
+ ActsAsTaggableOn.strict_case_match = true
140
+ expect {
141
+ expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('AWESOME', 'awesome').map(&:name)).to eq(%w(AWESOME awesome))
142
+ }.to change(ActsAsTaggableOn::Tag, :count).by(1)
143
+ end
60
144
  end
61
145
 
62
- it "should find or create by name" do
63
- lambda {
64
- Tag.find_or_create_all_with_like_by_name("awesome", "epic").map(&:name).should == ["awesome", "epic"]
65
- }.should change(Tag, :count).by(1)
146
+ it 'should find or create by name' do
147
+ expect {
148
+ expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('awesome', 'epic').map(&:name)).to eq(%w(awesome epic))
149
+ }.to change(ActsAsTaggableOn::Tag, :count).by(1)
66
150
  end
67
151
 
68
- it "should return an empty array if no tags are specified" do
69
- Tag.find_or_create_all_with_like_by_name([]).should == []
152
+ it 'should return an empty array if no tags are specified' do
153
+ expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name([])).to be_empty
70
154
  end
71
155
  end
72
156
 
73
- it "should require a name" do
157
+ it 'should require a name' do
74
158
  @tag.valid?
75
-
76
- if ActiveRecord::VERSION::MAJOR >= 3
77
- @tag.errors[:name].should == ["can't be blank"]
78
- else
79
- @tag.errors[:name].should == "can't be blank"
80
- end
159
+ #TODO, we should find another way to check this
160
+ expect(@tag.errors[:name]).to eq(["can't be blank"])
81
161
 
82
- @tag.name = "something"
162
+ @tag.name = 'something'
83
163
  @tag.valid?
84
-
85
- if ActiveRecord::VERSION::MAJOR >= 3
86
- @tag.errors[:name].should == []
87
- else
88
- @tag.errors[:name].should be_nil
89
- end
164
+
165
+ expect(@tag.errors[:name]).to be_empty
166
+ end
167
+
168
+ it 'should limit the name length to 255 or less characters' do
169
+ @tag.name = 'fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxranr'
170
+ @tag.valid?
171
+ #TODO, we should find another way to check this
172
+ expect(@tag.errors[:name]).to eq(['is too long (maximum is 255 characters)'])
173
+
174
+ @tag.name = 'fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxran'
175
+ @tag.valid?
176
+ expect(@tag.errors[:name]).to be_empty
90
177
  end
91
178
 
92
- it "should equal a tag with the same name" do
93
- @tag.name = "awesome"
94
- new_tag = Tag.new(:name => "awesome")
95
- new_tag.should == @tag
179
+ it 'should equal a tag with the same name' do
180
+ @tag.name = 'awesome'
181
+ new_tag = ActsAsTaggableOn::Tag.new(name: 'awesome')
182
+ expect(new_tag).to eq(@tag)
96
183
  end
97
184
 
98
- it "should return its name when to_s is called" do
99
- @tag.name = "cool"
100
- @tag.to_s.should == "cool"
185
+ it 'should return its name when to_s is called' do
186
+ @tag.name = 'cool'
187
+ expect(@tag.to_s).to eq('cool')
101
188
  end
102
189
 
103
- it "have named_scope named(something)" do
104
- @tag.name = "cool"
190
+ it 'have named_scope named(something)' do
191
+ @tag.name = 'cool'
105
192
  @tag.save!
106
- Tag.named('cool').should include(@tag)
193
+ expect(ActsAsTaggableOn::Tag.named('cool')).to include(@tag)
107
194
  end
108
195
 
109
- it "have named_scope named_like(something)" do
110
- @tag.name = "cool"
196
+ it 'have named_scope named_like(something)' do
197
+ @tag.name = 'cool'
111
198
  @tag.save!
112
- @another_tag = Tag.create!(:name => "coolip")
113
- Tag.named_like('cool').should include(@tag, @another_tag)
199
+ @another_tag = ActsAsTaggableOn::Tag.create!(name: 'coolip')
200
+ expect(ActsAsTaggableOn::Tag.named_like('cool')).to include(@tag, @another_tag)
201
+ end
202
+
203
+ describe 'escape wildcard symbols in like requests' do
204
+ before(:each) do
205
+ @tag.name = 'cool'
206
+ @tag.save
207
+ @another_tag = ActsAsTaggableOn::Tag.create!(name: 'coo%')
208
+ @another_tag2 = ActsAsTaggableOn::Tag.create!(name: 'coolish')
209
+ end
210
+
211
+ it "return escaped result when '%' char present in tag" do
212
+ expect(ActsAsTaggableOn::Tag.named_like('coo%')).to_not include(@tag)
213
+ expect(ActsAsTaggableOn::Tag.named_like('coo%')).to include(@another_tag)
214
+ end
215
+
216
+ end
217
+
218
+ describe 'when using strict_case_match' do
219
+ before do
220
+ ActsAsTaggableOn.strict_case_match = true
221
+ @tag.name = 'awesome'
222
+ @tag.save!
223
+ end
224
+
225
+ after do
226
+ ActsAsTaggableOn.strict_case_match = false
227
+ end
228
+
229
+ it 'should find by name' do
230
+ expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('awesome')).to eq(@tag)
231
+ end
232
+
233
+ context 'case sensitive' do
234
+ if using_case_insensitive_collation?
235
+ include_context 'without unique index'
236
+ end
237
+
238
+ it 'should find by name case sensitively' do
239
+ expect {
240
+ ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('AWESOME')
241
+ }.to change(ActsAsTaggableOn::Tag, :count)
242
+
243
+ expect(ActsAsTaggableOn::Tag.last.name).to eq('AWESOME')
244
+ end
245
+ end
246
+
247
+ context 'case sensitive' do
248
+ if using_case_insensitive_collation?
249
+ include_context 'without unique index'
250
+ end
251
+
252
+ it 'should have a named_scope named(something) that matches exactly' do
253
+ uppercase_tag = ActsAsTaggableOn::Tag.create(name: 'Cool')
254
+ @tag.name = 'cool'
255
+ @tag.save!
256
+
257
+ expect(ActsAsTaggableOn::Tag.named('cool')).to include(@tag)
258
+ expect(ActsAsTaggableOn::Tag.named('cool')).to_not include(uppercase_tag)
259
+ end
260
+ end
261
+
262
+ it 'should not change encoding' do
263
+ name = "\u3042"
264
+ original_encoding = name.encoding
265
+ record = ActsAsTaggableOn::Tag.find_or_create_with_like_by_name(name)
266
+ record.reload
267
+ expect(record.name.encoding).to eq(original_encoding)
268
+ end
269
+
270
+ context 'named any with some special characters combinations', if: using_mysql? do
271
+ it 'should not raise an invalid encoding exception' do
272
+ expect{ActsAsTaggableOn::Tag.named_any(["holä", "hol'ä"])}.not_to raise_error
273
+ end
274
+ end
275
+ end
276
+
277
+ describe 'name uniqeness validation' do
278
+ let(:duplicate_tag) { ActsAsTaggableOn::Tag.new(name: 'ror') }
279
+
280
+ before { ActsAsTaggableOn::Tag.create(name: 'ror') }
281
+
282
+ context "when don't need unique names" do
283
+ include_context 'without unique index'
284
+ it 'should not run uniqueness validation' do
285
+ allow(duplicate_tag).to receive(:validates_name_uniqueness?) { false }
286
+ duplicate_tag.save
287
+ expect(duplicate_tag).to be_persisted
288
+ end
289
+ end
290
+
291
+ context 'when do need unique names' do
292
+ it 'should run uniqueness validation' do
293
+ expect(duplicate_tag).to_not be_valid
294
+ end
295
+
296
+ it 'add error to name' do
297
+ duplicate_tag.save
298
+
299
+ expect(duplicate_tag.errors.size).to eq(1)
300
+ expect(duplicate_tag.errors.messages[:name]).to include('has already been taken')
301
+ end
302
+ end
303
+ end
304
+
305
+ describe 'popular tags' do
306
+ before do
307
+ %w(sports rails linux tennis golden_syrup).each_with_index do |t, i|
308
+ tag = ActsAsTaggableOn::Tag.new(name: t)
309
+ tag.taggings_count = i
310
+ tag.save!
311
+ end
312
+ end
313
+
314
+ it 'should find the most popular tags' do
315
+ expect(ActsAsTaggableOn::Tag.most_used(3).first.name).to eq("golden_syrup")
316
+ expect(ActsAsTaggableOn::Tag.most_used(3).length).to eq(3)
317
+ end
318
+
319
+ it 'should find the least popular tags' do
320
+ expect(ActsAsTaggableOn::Tag.least_used(3).first.name).to eq("sports")
321
+ expect(ActsAsTaggableOn::Tag.least_used(3).length).to eq(3)
322
+ end
114
323
  end
324
+
115
325
  end