acts-as-taggable-on 3.2.3 → 3.2.4
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 +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
|