acts-as-taggable-on 3.2.3 → 3.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -7
- data/Gemfile +0 -1
- data/acts-as-taggable-on.gemspec +6 -7
- data/gemfiles/activerecord_3.2.gemfile +0 -1
- data/gemfiles/activerecord_4.0.gemfile +0 -1
- data/gemfiles/activerecord_4.1.gemfile +0 -1
- data/gemfiles/activerecord_edge.gemfile +0 -1
- data/lib/acts-as-taggable-on.rb +26 -20
- data/lib/acts_as_taggable_on/{acts_as_taggable_on/compatibility.rb → compatibility.rb} +0 -0
- data/lib/acts_as_taggable_on/tag_list.rb +13 -77
- data/lib/acts_as_taggable_on/tag_list_parser.rb +78 -0
- data/lib/acts_as_taggable_on/taggable.rb +7 -6
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/cache.rb +0 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/collection.rb +0 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/core.rb +14 -10
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/dirty.rb +0 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/ownership.rb +1 -1
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/related.rb +0 -0
- data/lib/acts_as_taggable_on/utils.rb +2 -26
- data/lib/acts_as_taggable_on/version.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -0
- data/spec/acts_as_taggable_on/caching_spec.rb +1 -0
- data/spec/acts_as_taggable_on/related_spec.rb +1 -0
- data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +1 -0
- data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -40
- data/spec/acts_as_taggable_on/tag_spec.rb +29 -35
- data/spec/acts_as_taggable_on/taggable_spec.rb +55 -55
- data/spec/acts_as_taggable_on/tagger_spec.rb +1 -0
- data/spec/acts_as_taggable_on/tagging_spec.rb +2 -1
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -0
- data/spec/acts_as_taggable_on/utils_spec.rb +1 -0
- data/spec/internal/app/models/cached_model_with_array.rb +1 -1
- data/spec/internal/app/models/models.rb +2 -2
- data/spec/internal/db/schema.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/0-helpers.rb +32 -0
- metadata +36 -47
- data/spec/schema.rb +0 -82
File without changes
|
File without changes
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# This module is deprecated and will be removed in the incoming versions
|
2
|
+
|
1
3
|
module ActsAsTaggableOn
|
2
4
|
module Utils
|
3
5
|
class << self
|
@@ -10,33 +12,11 @@ module ActsAsTaggableOn
|
|
10
12
|
connection && connection.adapter_name == 'PostgreSQL'
|
11
13
|
end
|
12
14
|
|
13
|
-
def postgresql_version
|
14
|
-
if using_postgresql?
|
15
|
-
connection.execute('SHOW SERVER_VERSION').first['server_version'].to_f
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def postgresql_support_json?
|
20
|
-
postgresql_version >= 9.2
|
21
|
-
end
|
22
|
-
|
23
|
-
def using_sqlite?
|
24
|
-
connection && connection.adapter_name == 'SQLite'
|
25
|
-
end
|
26
|
-
|
27
15
|
def using_mysql?
|
28
16
|
#We should probably use regex for mysql to support prehistoric adapters
|
29
17
|
connection && connection.adapter_name == 'Mysql2'
|
30
18
|
end
|
31
19
|
|
32
|
-
def using_case_insensitive_collation?
|
33
|
-
using_mysql? && connection.collation =~ /_ci\Z/
|
34
|
-
end
|
35
|
-
|
36
|
-
def supports_concurrency?
|
37
|
-
!using_sqlite?
|
38
|
-
end
|
39
|
-
|
40
20
|
def sha_prefix(string)
|
41
21
|
Digest::SHA1.hexdigest("#{string}#{rand}")[0..6]
|
42
22
|
end
|
@@ -45,10 +25,6 @@ module ActsAsTaggableOn
|
|
45
25
|
::ActiveRecord::VERSION::MAJOR == 4
|
46
26
|
end
|
47
27
|
|
48
|
-
def active_record42?
|
49
|
-
active_record4? && ::ActiveRecord::VERSION::MINOR >= 2
|
50
|
-
end
|
51
|
-
|
52
28
|
def like_operator
|
53
29
|
using_postgresql? ? 'ILIKE' : 'LIKE'
|
54
30
|
end
|
@@ -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,4 +1,5 @@
|
|
1
|
-
# encoding: utf-8
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
2
3
|
require 'spec_helper'
|
3
4
|
|
4
5
|
describe ActsAsTaggableOn::TagList do
|
@@ -7,9 +8,7 @@ describe ActsAsTaggableOn::TagList do
|
|
7
8
|
|
8
9
|
it { should be_kind_of Array }
|
9
10
|
|
10
|
-
|
11
|
-
expect(ActsAsTaggableOn::TagList.from([])).to be_empty
|
12
|
-
end
|
11
|
+
|
13
12
|
|
14
13
|
describe '#add' do
|
15
14
|
it 'should be able to be add a new tag word' do
|
@@ -118,41 +117,5 @@ describe ActsAsTaggableOn::TagList do
|
|
118
117
|
|
119
118
|
end
|
120
119
|
|
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
|
156
|
-
end
|
157
120
|
|
158
121
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# encoding: utf-8
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'db/migrate/2_add_missing_unique_indices.rb'
|
4
4
|
|
@@ -18,21 +18,19 @@ describe ActsAsTaggableOn::Tag do
|
|
18
18
|
|
19
19
|
|
20
20
|
describe 'named like any' do
|
21
|
-
if
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
expect(ActsAsTaggableOn::Tag.named_like_any(%w(awesome epic)).count).to eq(2)
|
30
|
-
end
|
21
|
+
context 'case insensitive collation and unique index on tag name', if: using_case_insensitive_collation? do
|
22
|
+
before(:each) do
|
23
|
+
ActsAsTaggableOn::Tag.create(name: 'Awesome')
|
24
|
+
ActsAsTaggableOn::Tag.create(name: 'epic')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should find both tags' do
|
28
|
+
expect(ActsAsTaggableOn::Tag.named_like_any(%w(awesome epic)).count).to eq(2)
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
34
32
|
context 'case insensitive collation without indexes or case sensitive collation with indexes' do
|
35
|
-
if
|
33
|
+
if using_case_insensitive_collation?
|
36
34
|
include_context 'without unique index'
|
37
35
|
end
|
38
36
|
|
@@ -69,28 +67,24 @@ describe ActsAsTaggableOn::Tag do
|
|
69
67
|
end
|
70
68
|
end
|
71
69
|
|
72
|
-
unless
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
70
|
+
describe 'find or create by unicode name', unless: using_sqlite? do
|
71
|
+
before(:each) do
|
72
|
+
@tag.name = 'привет'
|
73
|
+
@tag.save
|
74
|
+
end
|
78
75
|
|
79
|
-
|
80
|
-
|
81
|
-
|
76
|
+
it 'should find by name' do
|
77
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('привет')).to eq(@tag)
|
78
|
+
end
|
82
79
|
|
83
|
-
|
84
|
-
|
85
|
-
|
80
|
+
it 'should find by name case insensitive' do
|
81
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('ПРИВЕТ')).to eq(@tag)
|
82
|
+
end
|
86
83
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('Iñupiat')).to eq(@tag)
|
92
|
-
end
|
93
|
-
end
|
84
|
+
it 'should find by name accent insensitive', if: using_case_insensitive_collation? do
|
85
|
+
@tag.name = 'inupiat'
|
86
|
+
@tag.save
|
87
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('Iñupiat')).to eq(@tag)
|
94
88
|
end
|
95
89
|
end
|
96
90
|
|
@@ -109,7 +103,7 @@ describe ActsAsTaggableOn::Tag do
|
|
109
103
|
end
|
110
104
|
|
111
105
|
context 'case sensitive' do
|
112
|
-
if
|
106
|
+
if using_case_insensitive_collation?
|
113
107
|
include_context 'without unique index'
|
114
108
|
end
|
115
109
|
|
@@ -128,7 +122,7 @@ describe ActsAsTaggableOn::Tag do
|
|
128
122
|
end
|
129
123
|
|
130
124
|
context 'case sensitive' do
|
131
|
-
if
|
125
|
+
if using_case_insensitive_collation?
|
132
126
|
include_context 'without unique index'
|
133
127
|
end
|
134
128
|
|
@@ -228,7 +222,7 @@ describe ActsAsTaggableOn::Tag do
|
|
228
222
|
end
|
229
223
|
|
230
224
|
context 'case sensitive' do
|
231
|
-
if
|
225
|
+
if using_case_insensitive_collation?
|
232
226
|
include_context 'without unique index'
|
233
227
|
end
|
234
228
|
|
@@ -242,7 +236,7 @@ describe ActsAsTaggableOn::Tag do
|
|
242
236
|
end
|
243
237
|
|
244
238
|
context 'case sensitive' do
|
245
|
-
if
|
239
|
+
if using_case_insensitive_collation?
|
246
240
|
include_context 'without unique index'
|
247
241
|
end
|
248
242
|
|
@@ -208,6 +208,14 @@ describe 'Taggable' do
|
|
208
208
|
expect(TaggableModel.tagged_with('ruby').group(:created_at).count.count).to eq(1)
|
209
209
|
end
|
210
210
|
|
211
|
+
it 'can be used as scope' do
|
212
|
+
@taggable.skill_list = 'ruby'
|
213
|
+
@taggable.save
|
214
|
+
untaggable_model = @taggable.untaggable_models.create!(name:'foobar')
|
215
|
+
scope_tag = TaggableModel.tagged_with('ruby', any: 'distinct', order: 'taggable_models.name asc')
|
216
|
+
expect(UntaggableModel.joins(:taggable_model).merge(scope_tag).except(:select)).to eq([untaggable_model])
|
217
|
+
end
|
218
|
+
|
211
219
|
it "shouldn't generate a query with DISTINCT by default" do
|
212
220
|
@taggable.skill_list = 'ruby, rails, css'
|
213
221
|
@taggable.save
|
@@ -255,16 +263,14 @@ describe 'Taggable' do
|
|
255
263
|
expect(TaggableModel.select('distinct(taggable_models.id), taggable_models.*').joins(:untaggable_models).tagged_with(['rails', 'ruby'], :any => false).to_a.sort).to eq([bob, frank].sort)
|
256
264
|
end
|
257
265
|
|
258
|
-
unless
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
TaggableModel.create(name: 'Katia', tag_list: 'ПРИВЕТ')
|
266
|
+
it 'should not care about case for unicode names', unless: using_sqlite? do
|
267
|
+
ActsAsTaggableOn.strict_case_match = false
|
268
|
+
TaggableModel.create(name: 'Anya', tag_list: 'ПРИВЕТ')
|
269
|
+
TaggableModel.create(name: 'Igor', tag_list: 'привет')
|
270
|
+
TaggableModel.create(name: 'Katia', tag_list: 'ПРИВЕТ')
|
264
271
|
|
265
|
-
|
266
|
-
|
267
|
-
end
|
272
|
+
expect(ActsAsTaggableOn::Tag.all.size).to eq(1)
|
273
|
+
expect(TaggableModel.tagged_with('привет').to_a).to eq(TaggableModel.tagged_with('ПРИВЕТ').to_a)
|
268
274
|
end
|
269
275
|
|
270
276
|
context 'should be able to create and find tags in languages without capitalization :' do
|
@@ -586,28 +592,26 @@ describe 'Taggable' do
|
|
586
592
|
|
587
593
|
end
|
588
594
|
|
589
|
-
if
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
connor.save
|
606
|
-
end
|
595
|
+
xit 'should not duplicate tags added on different threads', if: supports_concurrency?, skip: 'FIXME, Deadlocks in travis' do
|
596
|
+
#TODO, try with more threads and fix deadlock
|
597
|
+
thread_count = 4
|
598
|
+
barrier = Barrier.new thread_count
|
599
|
+
|
600
|
+
expect {
|
601
|
+
thread_count.times.map do |idx|
|
602
|
+
Thread.start do
|
603
|
+
connor = TaggableModel.first_or_create(name: 'Connor')
|
604
|
+
connor.tag_list = 'There, can, be, only, one'
|
605
|
+
barrier.wait
|
606
|
+
begin
|
607
|
+
connor.save
|
608
|
+
rescue ActsAsTaggableOn::DuplicateTagError
|
609
|
+
# second save should succeed
|
610
|
+
connor.save
|
607
611
|
end
|
608
|
-
end
|
609
|
-
|
610
|
-
|
612
|
+
end
|
613
|
+
end.map(&:join)
|
614
|
+
}.to change(ActsAsTaggableOn::Tag, :count).by(5)
|
611
615
|
end
|
612
616
|
end
|
613
617
|
|
@@ -853,34 +857,30 @@ describe 'Taggable' do
|
|
853
857
|
end
|
854
858
|
end
|
855
859
|
|
856
|
-
if
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
@taggables = [@taggable, TaggableModelWithJson.new(:name => 'John Doe')]
|
862
|
-
end
|
860
|
+
describe 'Taggable model with json columns', if: postgresql_support_json? do
|
861
|
+
before(:each) do
|
862
|
+
@taggable = TaggableModelWithJson.new(:name => 'Bob Jones')
|
863
|
+
@taggables = [@taggable, TaggableModelWithJson.new(:name => 'John Doe')]
|
864
|
+
end
|
863
865
|
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
866
|
+
it 'should be able to find by tag with context' do
|
867
|
+
@taggable.skill_list = 'ruby, rails, css'
|
868
|
+
@taggable.tag_list = 'bob, charlie'
|
869
|
+
@taggable.save
|
868
870
|
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
871
|
+
expect(TaggableModelWithJson.tagged_with('ruby').first).to eq(@taggable)
|
872
|
+
expect(TaggableModelWithJson.tagged_with('ruby, css').first).to eq(@taggable)
|
873
|
+
expect(TaggableModelWithJson.tagged_with('bob', :on => :skills).first).to_not eq(@taggable)
|
874
|
+
expect(TaggableModelWithJson.tagged_with('bob', :on => :tags).first).to eq(@taggable)
|
875
|
+
end
|
874
876
|
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
877
|
+
it 'should be able to find tagged with any tag' do
|
878
|
+
bob = TaggableModelWithJson.create(:name => 'Bob', :tag_list => 'fitter, happier, more productive', :skill_list => 'ruby, rails, css')
|
879
|
+
frank = TaggableModelWithJson.create(:name => 'Frank', :tag_list => 'weaker, depressed, inefficient', :skill_list => 'ruby, rails, css')
|
880
|
+
steve = TaggableModelWithJson.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
|
879
881
|
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
end
|
884
|
-
end
|
882
|
+
expect(TaggableModelWithJson.tagged_with(%w(ruby java), :order => 'taggable_model_with_jsons.name', :any => true).to_a).to eq([bob, frank, steve])
|
883
|
+
expect(TaggableModelWithJson.tagged_with(%w(c++ fitter), :order => 'taggable_model_with_jsons.name', :any => true).to_a).to eq([bob, steve])
|
884
|
+
expect(TaggableModelWithJson.tagged_with(%w(depressed css), :order => 'taggable_model_with_jsons.name', :any => true).to_a).to eq([bob, frank])
|
885
885
|
end
|
886
886
|
end
|