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
|
@@ -1,115 +1,295 @@
|
|
|
1
|
-
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
require 'db/migrate/2_add_missing_unique_indices.rb'
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
shared_examples_for 'without unique index' do
|
|
6
|
+
prepend_before(:all) { AddMissingUniqueIndices.down }
|
|
7
|
+
append_after(:all) do
|
|
8
|
+
ActsAsTaggableOn::Tag.delete_all
|
|
9
|
+
AddMissingUniqueIndices.up
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe ActsAsTaggableOn::Tag do
|
|
4
14
|
before(:each) do
|
|
5
|
-
|
|
6
|
-
@
|
|
7
|
-
@user = TaggableModel.create(:name => "Pablo")
|
|
15
|
+
@tag = ActsAsTaggableOn::Tag.new
|
|
16
|
+
@user = TaggableModel.create(name: 'Pablo')
|
|
8
17
|
end
|
|
9
18
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
|
|
20
|
+
describe 'named like any' do
|
|
21
|
+
if ActsAsTaggableOn::Utils.using_case_insensitive_collation?
|
|
22
|
+
context 'case insensitive collation and unique index on tag name' 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
|
|
14
32
|
end
|
|
15
33
|
|
|
16
|
-
|
|
17
|
-
|
|
34
|
+
context 'case insensitive collation without indexes or case sensitive collation with indexes' do
|
|
35
|
+
if ActsAsTaggableOn::Utils.using_case_insensitive_collation?
|
|
36
|
+
include_context 'without unique index'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
before(:each) do
|
|
40
|
+
ActsAsTaggableOn::Tag.create(name: 'Awesome')
|
|
41
|
+
ActsAsTaggableOn::Tag.create(name: 'awesome')
|
|
42
|
+
ActsAsTaggableOn::Tag.create(name: 'epic')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should find both tags' do
|
|
46
|
+
expect(ActsAsTaggableOn::Tag.named_like_any(%w(awesome epic)).count).to eq(3)
|
|
47
|
+
end
|
|
18
48
|
end
|
|
19
49
|
end
|
|
20
50
|
|
|
21
|
-
describe
|
|
51
|
+
describe 'find or create by name' do
|
|
22
52
|
before(:each) do
|
|
23
|
-
@tag.name =
|
|
53
|
+
@tag.name = 'awesome'
|
|
24
54
|
@tag.save
|
|
25
55
|
end
|
|
26
56
|
|
|
27
|
-
it
|
|
28
|
-
Tag.find_or_create_with_like_by_name(
|
|
57
|
+
it 'should find by name' do
|
|
58
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('awesome')).to eq(@tag)
|
|
29
59
|
end
|
|
30
60
|
|
|
31
|
-
it
|
|
32
|
-
Tag.find_or_create_with_like_by_name(
|
|
61
|
+
it 'should find by name case insensitive' do
|
|
62
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('AWESOME')).to eq(@tag)
|
|
33
63
|
end
|
|
34
64
|
|
|
35
|
-
it
|
|
36
|
-
|
|
37
|
-
Tag.find_or_create_with_like_by_name(
|
|
38
|
-
}.
|
|
65
|
+
it 'should create by name' do
|
|
66
|
+
expect(-> {
|
|
67
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('epic')
|
|
68
|
+
}).to change(ActsAsTaggableOn::Tag, :count).by(1)
|
|
39
69
|
end
|
|
40
70
|
end
|
|
41
71
|
|
|
42
|
-
|
|
72
|
+
unless ActsAsTaggableOn::Utils.using_sqlite?
|
|
73
|
+
describe 'find or create by unicode name' do
|
|
74
|
+
before(:each) do
|
|
75
|
+
@tag.name = 'привет'
|
|
76
|
+
@tag.save
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'should find by name' do
|
|
80
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('привет')).to eq(@tag)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'should find by name case insensitive' do
|
|
84
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('ПРИВЕТ')).to eq(@tag)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if ActsAsTaggableOn::Utils.using_case_insensitive_collation?
|
|
88
|
+
it 'should find by name accent insensitive' do
|
|
89
|
+
@tag.name = 'inupiat'
|
|
90
|
+
@tag.save
|
|
91
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('Iñupiat')).to eq(@tag)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe 'find or create all by any name' do
|
|
43
98
|
before(:each) do
|
|
44
|
-
@tag.name =
|
|
99
|
+
@tag.name = 'awesome'
|
|
45
100
|
@tag.save
|
|
46
101
|
end
|
|
47
102
|
|
|
48
|
-
it
|
|
49
|
-
Tag.find_or_create_all_with_like_by_name(
|
|
103
|
+
it 'should find by name' do
|
|
104
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('awesome')).to eq([@tag])
|
|
50
105
|
end
|
|
51
106
|
|
|
52
|
-
it
|
|
53
|
-
Tag.find_or_create_all_with_like_by_name(
|
|
107
|
+
it 'should find by name case insensitive' do
|
|
108
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('AWESOME')).to eq([@tag])
|
|
54
109
|
end
|
|
55
110
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
111
|
+
context 'case sensitive' do
|
|
112
|
+
if ActsAsTaggableOn::Utils.using_case_insensitive_collation?
|
|
113
|
+
include_context 'without unique index'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'should find by name case sensitive' do
|
|
117
|
+
ActsAsTaggableOn.strict_case_match = true
|
|
118
|
+
expect {
|
|
119
|
+
ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('AWESOME')
|
|
120
|
+
}.to change(ActsAsTaggableOn::Tag, :count).by(1)
|
|
121
|
+
end
|
|
60
122
|
end
|
|
61
123
|
|
|
62
|
-
it
|
|
63
|
-
|
|
64
|
-
Tag.find_or_create_all_with_like_by_name(
|
|
65
|
-
}.
|
|
124
|
+
it 'should create by name' do
|
|
125
|
+
expect {
|
|
126
|
+
ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('epic')
|
|
127
|
+
}.to change(ActsAsTaggableOn::Tag, :count).by(1)
|
|
66
128
|
end
|
|
67
129
|
|
|
68
|
-
|
|
69
|
-
|
|
130
|
+
context 'case sensitive' do
|
|
131
|
+
if ActsAsTaggableOn::Utils.using_case_insensitive_collation?
|
|
132
|
+
include_context 'without unique index'
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'should find or create by name case sensitive' do
|
|
136
|
+
ActsAsTaggableOn.strict_case_match = true
|
|
137
|
+
expect {
|
|
138
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('AWESOME', 'awesome').map(&:name)).to eq(%w(AWESOME awesome))
|
|
139
|
+
}.to change(ActsAsTaggableOn::Tag, :count).by(1)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'should find or create by name' do
|
|
144
|
+
expect {
|
|
145
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name('awesome', 'epic').map(&:name)).to eq(%w(awesome epic))
|
|
146
|
+
}.to change(ActsAsTaggableOn::Tag, :count).by(1)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'should return an empty array if no tags are specified' do
|
|
150
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name([])).to be_empty
|
|
70
151
|
end
|
|
71
152
|
end
|
|
72
153
|
|
|
73
|
-
it
|
|
154
|
+
it 'should require a name' do
|
|
74
155
|
@tag.valid?
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@tag.errors[:name].should == ["can't be blank"]
|
|
78
|
-
else
|
|
79
|
-
@tag.errors[:name].should == "can't be blank"
|
|
80
|
-
end
|
|
156
|
+
#TODO, we should find another way to check this
|
|
157
|
+
expect(@tag.errors[:name]).to eq(["can't be blank"])
|
|
81
158
|
|
|
82
|
-
@tag.name =
|
|
159
|
+
@tag.name = 'something'
|
|
83
160
|
@tag.valid?
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
@tag.errors[:name].should == []
|
|
87
|
-
else
|
|
88
|
-
@tag.errors[:name].should be_nil
|
|
89
|
-
end
|
|
161
|
+
|
|
162
|
+
expect(@tag.errors[:name]).to be_empty
|
|
90
163
|
end
|
|
91
164
|
|
|
92
|
-
it
|
|
93
|
-
@tag.name =
|
|
94
|
-
|
|
95
|
-
|
|
165
|
+
it 'should limit the name length to 255 or less characters' do
|
|
166
|
+
@tag.name = 'fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxranr'
|
|
167
|
+
@tag.valid?
|
|
168
|
+
#TODO, we should find another way to check this
|
|
169
|
+
expect(@tag.errors[:name]).to eq(['is too long (maximum is 255 characters)'])
|
|
170
|
+
|
|
171
|
+
@tag.name = 'fgkgnkkgjymkypbuozmwwghblmzpqfsgjasflblywhgkwndnkzeifalfcpeaeqychjuuowlacmuidnnrkprgpcpybarbkrmziqihcrxirlokhnzfvmtzixgvhlxzncyywficpraxfnjptxxhkqmvicbcdcynkjvziefqzyndxkjmsjlvyvbwraklbalykyxoliqdlreeykuphdtmzfdwpphmrqvwvqffojkqhlzvinqajsxbszyvrqqyzusxran'
|
|
172
|
+
@tag.valid?
|
|
173
|
+
expect(@tag.errors[:name]).to be_empty
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'should equal a tag with the same name' do
|
|
177
|
+
@tag.name = 'awesome'
|
|
178
|
+
new_tag = ActsAsTaggableOn::Tag.new(name: 'awesome')
|
|
179
|
+
expect(new_tag).to eq(@tag)
|
|
96
180
|
end
|
|
97
181
|
|
|
98
|
-
it
|
|
99
|
-
@tag.name =
|
|
100
|
-
@tag.to_s.
|
|
182
|
+
it 'should return its name when to_s is called' do
|
|
183
|
+
@tag.name = 'cool'
|
|
184
|
+
expect(@tag.to_s).to eq('cool')
|
|
101
185
|
end
|
|
102
186
|
|
|
103
|
-
it
|
|
104
|
-
@tag.name =
|
|
187
|
+
it 'have named_scope named(something)' do
|
|
188
|
+
@tag.name = 'cool'
|
|
105
189
|
@tag.save!
|
|
106
|
-
Tag.named('cool').
|
|
190
|
+
expect(ActsAsTaggableOn::Tag.named('cool')).to include(@tag)
|
|
107
191
|
end
|
|
108
192
|
|
|
109
|
-
it
|
|
110
|
-
@tag.name =
|
|
193
|
+
it 'have named_scope named_like(something)' do
|
|
194
|
+
@tag.name = 'cool'
|
|
111
195
|
@tag.save!
|
|
112
|
-
@another_tag = Tag.create!(:
|
|
113
|
-
Tag.named_like('cool').
|
|
196
|
+
@another_tag = ActsAsTaggableOn::Tag.create!(name: 'coolip')
|
|
197
|
+
expect(ActsAsTaggableOn::Tag.named_like('cool')).to include(@tag, @another_tag)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
describe 'escape wildcard symbols in like requests' do
|
|
201
|
+
before(:each) do
|
|
202
|
+
@tag.name = 'cool'
|
|
203
|
+
@tag.save
|
|
204
|
+
@another_tag = ActsAsTaggableOn::Tag.create!(name: 'coo%')
|
|
205
|
+
@another_tag2 = ActsAsTaggableOn::Tag.create!(name: 'coolish')
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "return escaped result when '%' char present in tag" do
|
|
209
|
+
expect(ActsAsTaggableOn::Tag.named_like('coo%')).to_not include(@tag)
|
|
210
|
+
expect(ActsAsTaggableOn::Tag.named_like('coo%')).to include(@another_tag)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
describe 'when using strict_case_match' do
|
|
216
|
+
before do
|
|
217
|
+
ActsAsTaggableOn.strict_case_match = true
|
|
218
|
+
@tag.name = 'awesome'
|
|
219
|
+
@tag.save!
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
after do
|
|
223
|
+
ActsAsTaggableOn.strict_case_match = false
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
it 'should find by name' do
|
|
227
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('awesome')).to eq(@tag)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
context 'case sensitive' do
|
|
231
|
+
if ActsAsTaggableOn::Utils.using_case_insensitive_collation?
|
|
232
|
+
include_context 'without unique index'
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'should find by name case sensitively' do
|
|
236
|
+
expect {
|
|
237
|
+
ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('AWESOME')
|
|
238
|
+
}.to change(ActsAsTaggableOn::Tag, :count)
|
|
239
|
+
|
|
240
|
+
expect(ActsAsTaggableOn::Tag.last.name).to eq('AWESOME')
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
context 'case sensitive' do
|
|
245
|
+
if ActsAsTaggableOn::Utils.using_case_insensitive_collation?
|
|
246
|
+
include_context 'without unique index'
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
it 'should have a named_scope named(something) that matches exactly' do
|
|
250
|
+
uppercase_tag = ActsAsTaggableOn::Tag.create(name: 'Cool')
|
|
251
|
+
@tag.name = 'cool'
|
|
252
|
+
@tag.save!
|
|
253
|
+
|
|
254
|
+
expect(ActsAsTaggableOn::Tag.named('cool')).to include(@tag)
|
|
255
|
+
expect(ActsAsTaggableOn::Tag.named('cool')).to_not include(uppercase_tag)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it 'should not change enconding' do
|
|
260
|
+
name = "\u3042"
|
|
261
|
+
original_encoding = name.encoding
|
|
262
|
+
record = ActsAsTaggableOn::Tag.find_or_create_with_like_by_name(name)
|
|
263
|
+
record.reload
|
|
264
|
+
expect(record.name.encoding).to eq(original_encoding)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
describe 'name uniqeness validation' do
|
|
269
|
+
let(:duplicate_tag) { ActsAsTaggableOn::Tag.new(name: 'ror') }
|
|
270
|
+
|
|
271
|
+
before { ActsAsTaggableOn::Tag.create(name: 'ror') }
|
|
272
|
+
|
|
273
|
+
context "when don't need unique names" do
|
|
274
|
+
include_context 'without unique index'
|
|
275
|
+
it 'should not run uniqueness validation' do
|
|
276
|
+
allow(duplicate_tag).to receive(:validates_name_uniqueness?) { false }
|
|
277
|
+
duplicate_tag.save
|
|
278
|
+
expect(duplicate_tag).to be_persisted
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
context 'when do need unique names' do
|
|
283
|
+
it 'should run uniqueness validation' do
|
|
284
|
+
expect(duplicate_tag).to_not be_valid
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it 'add error to name' do
|
|
288
|
+
duplicate_tag.save
|
|
289
|
+
|
|
290
|
+
expect(duplicate_tag.errors.size).to eq(1)
|
|
291
|
+
expect(duplicate_tag.errors.messages[:name]).to include('has already been taken')
|
|
292
|
+
end
|
|
293
|
+
end
|
|
114
294
|
end
|
|
115
295
|
end
|