acts-as-taggable-on 3.2.3 → 3.3.0
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/Appraisals +0 -4
- data/Gemfile +0 -1
- data/README.md +1 -1
- data/acts-as-taggable-on.gemspec +6 -8
- data/db/migrate/4_add_missing_taggable_index.rb +9 -0
- data/gemfiles/activerecord_3.2.gemfile +0 -2
- data/gemfiles/activerecord_4.0.gemfile +0 -2
- data/gemfiles/activerecord_4.1.gemfile +0 -2
- data/gemfiles/activerecord_edge.gemfile +0 -2
- data/lib/acts-as-taggable-on.rb +28 -20
- 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/{acts_as_taggable_on → taggable}/core.rb +46 -23
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/ownership.rb +1 -1
- data/lib/acts_as_taggable_on/taggable.rb +7 -6
- data/lib/acts_as_taggable_on/tagging.rb +1 -1
- data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
- 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/dirty_spec.rb +127 -0
- data/spec/acts_as_taggable_on/taggable_spec.rb +76 -163
- data/spec/acts_as_taggable_on/tagger_spec.rb +1 -1
- data/spec/acts_as_taggable_on/tagging_spec.rb +13 -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 +39 -67
- data/spec/schema.rb +0 -82
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on/compatibility.rb → compatibility.rb} +0 -0
- /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}/dirty.rb +0 -0
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/related.rb +0 -0
|
@@ -4,10 +4,10 @@ module ActsAsTaggableOn
|
|
|
4
4
|
def tag_cloud(tags, classes)
|
|
5
5
|
return [] if tags.empty?
|
|
6
6
|
|
|
7
|
-
max_count = tags.sort_by(&:
|
|
7
|
+
max_count = tags.sort_by(&:taggings_count).last.taggings_count.to_f
|
|
8
8
|
|
|
9
9
|
tags.each do |tag|
|
|
10
|
-
index = ((tag.
|
|
10
|
+
index = ((tag.taggings_count / max_count) * (classes.size - 1))
|
|
11
11
|
yield tag, classes[index.nan? ? 0 : index.round]
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -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
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe ActsAsTaggableOn::Taggable::Dirty do
|
|
5
|
+
context 'with un-contexted tags' do
|
|
6
|
+
before(:each) do
|
|
7
|
+
@taggable = TaggableModel.create(tag_list: 'awesome, epic')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
context 'when tag_list changed' do
|
|
11
|
+
before(:each) do
|
|
12
|
+
expect(@taggable.changes).to be_empty
|
|
13
|
+
@taggable.tag_list = 'one'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'should show changes of dirty object' do
|
|
17
|
+
expect(@taggable.changes).to eq({'tag_list' => ['awesome, epic', ['one']]})
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'flags tag_list as changed' do
|
|
21
|
+
expect(@taggable.tag_list_changed?).to be_truthy
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'preserves original value' do
|
|
25
|
+
expect(@taggable.tag_list_was).to eq('awesome, epic')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'shows what the change was' do
|
|
29
|
+
expect(@taggable.tag_list_change).to eq(['awesome, epic', ['one']])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context 'without order' do
|
|
33
|
+
it 'should not mark attribute if order change ' do
|
|
34
|
+
taggable = TaggableModel.create(name: 'Dirty Harry', tag_list: %w(d c b a))
|
|
35
|
+
taggable.tag_list = %w(a b c d)
|
|
36
|
+
expect(taggable.tag_list_changed?).to be_falsey
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'with order' do
|
|
41
|
+
it 'should mark attribute if order change' do
|
|
42
|
+
taggable = OrderedTaggableModel.create(name: 'Clean Harry', tag_list: 'd,c,b,a')
|
|
43
|
+
taggable.save
|
|
44
|
+
taggable.tag_list = %w(a b c d)
|
|
45
|
+
expect(taggable.tag_list_changed?).to be_truthy
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context 'when tag_list is the same' do
|
|
51
|
+
before(:each) do
|
|
52
|
+
@taggable.tag_list = 'awesome, epic'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'is not flagged as changed' do
|
|
56
|
+
expect(@taggable.tag_list_changed?).to be_falsy
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'does not show any changes to the taggable item' do
|
|
60
|
+
expect(@taggable.changes).to be_empty
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context "and using a delimiter different from a ','" do
|
|
64
|
+
before do
|
|
65
|
+
@old_delimiter = ActsAsTaggableOn.delimiter
|
|
66
|
+
ActsAsTaggableOn.delimiter = ';'
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
after do
|
|
70
|
+
ActsAsTaggableOn.delimiter = @old_delimiter
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'does not show any changes to the taggable item when using array assignments' do
|
|
74
|
+
@taggable.tag_list = %w(awesome epic)
|
|
75
|
+
expect(@taggable.changes).to be_empty
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context 'with context tags' do
|
|
82
|
+
before(:each) do
|
|
83
|
+
@taggable = TaggableModel.create('language_list' => 'awesome, epic')
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context 'when language_list changed' do
|
|
87
|
+
before(:each) do
|
|
88
|
+
expect(@taggable.changes).to be_empty
|
|
89
|
+
@taggable.language_list = 'one'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'should show changes of dirty object' do
|
|
93
|
+
expect(@taggable.changes).to eq({'language_list' => ['awesome, epic', ['one']]})
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'flags language_list as changed' do
|
|
97
|
+
expect(@taggable.language_list_changed?).to be_truthy
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'preserves original value' do
|
|
101
|
+
expect(@taggable.language_list_was).to eq('awesome, epic')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'shows what the change was' do
|
|
105
|
+
expect(@taggable.language_list_change).to eq(['awesome, epic', ['one']])
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'shows what the changes were' do
|
|
109
|
+
expect(@taggable.language_list_changes).to eq(['awesome, epic', ['one']])
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context 'when language_list is the same' do
|
|
114
|
+
before(:each) do
|
|
115
|
+
@taggable.language_list = 'awesome, epic'
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'is not flagged as changed' do
|
|
119
|
+
expect(@taggable.language_list_changed?).to be_falsy
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'does not show any changes to the taggable item' do
|
|
123
|
+
expect(@taggable.changes).to be_empty
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|