acts-as-taggable-on 3.2.1 → 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 +21 -1
- data/Appraisals +0 -4
- data/CHANGELOG.md +27 -15
- data/README.md +18 -11
- data/acts-as-taggable-on.gemspec +0 -2
- data/db/migrate/4_add_missing_taggable_index.rb +9 -0
- 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 +28 -20
- data/lib/acts_as_taggable_on/tag.rb +1 -1
- data/lib/acts_as_taggable_on/tag_list.rb +15 -78
- data/lib/acts_as_taggable_on/tag_list_parser.rb +78 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/cache.rb +1 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/collection.rb +2 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/core.rb +68 -44
- 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 +6 -6
- data/lib/acts_as_taggable_on/taggable.rb +7 -8
- data/lib/acts_as_taggable_on/tagging.rb +7 -1
- data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
- data/lib/acts_as_taggable_on/utils.rb +26 -50
- 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 +34 -38
- data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
- data/spec/acts_as_taggable_on/taggable_spec.rb +84 -164
- data/spec/acts_as_taggable_on/tagger_spec.rb +1 -1
- data/spec/acts_as_taggable_on/tagging_spec.rb +37 -1
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -0
- data/spec/acts_as_taggable_on/utils_spec.rb +6 -11
- 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
- data/spec/support/database_cleaner.rb +4 -0
- metadata +17 -47
- data/spec/bm.rb +0 -52
- 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}/dirty.rb +0 -0
|
@@ -1,10 +1,13 @@
|
|
|
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
|
|
|
5
5
|
shared_examples_for 'without unique index' do
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
prepend_before(:all) { AddMissingUniqueIndices.down }
|
|
7
|
+
append_after(:all) do
|
|
8
|
+
ActsAsTaggableOn::Tag.delete_all
|
|
9
|
+
AddMissingUniqueIndices.up
|
|
10
|
+
end
|
|
8
11
|
end
|
|
9
12
|
|
|
10
13
|
describe ActsAsTaggableOn::Tag do
|
|
@@ -14,23 +17,20 @@ describe ActsAsTaggableOn::Tag do
|
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
|
|
18
20
|
describe 'named like any' do
|
|
19
|
-
context 'case insensitive collation and unique index on tag name' do
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
expect(ActsAsTaggableOn::Tag.named_like_any(%w(awesome epic)).count).to eq(2)
|
|
28
|
-
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)
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
context 'case insensitive collation without indexes or case sensitive collation with indexes' do
|
|
33
|
-
if
|
|
33
|
+
if using_case_insensitive_collation?
|
|
34
34
|
include_context 'without unique index'
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -67,28 +67,24 @@ describe ActsAsTaggableOn::Tag do
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
unless
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
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
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
it 'should find by name' do
|
|
77
|
+
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('привет')).to eq(@tag)
|
|
78
|
+
end
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
expect(ActsAsTaggableOn::Tag.find_or_create_with_like_by_name('Iñupiat')).to eq(@tag)
|
|
90
|
-
end
|
|
91
|
-
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)
|
|
92
88
|
end
|
|
93
89
|
end
|
|
94
90
|
|
|
@@ -107,7 +103,7 @@ describe ActsAsTaggableOn::Tag do
|
|
|
107
103
|
end
|
|
108
104
|
|
|
109
105
|
context 'case sensitive' do
|
|
110
|
-
if
|
|
106
|
+
if using_case_insensitive_collation?
|
|
111
107
|
include_context 'without unique index'
|
|
112
108
|
end
|
|
113
109
|
|
|
@@ -126,7 +122,7 @@ describe ActsAsTaggableOn::Tag do
|
|
|
126
122
|
end
|
|
127
123
|
|
|
128
124
|
context 'case sensitive' do
|
|
129
|
-
if
|
|
125
|
+
if using_case_insensitive_collation?
|
|
130
126
|
include_context 'without unique index'
|
|
131
127
|
end
|
|
132
128
|
|
|
@@ -226,7 +222,7 @@ describe ActsAsTaggableOn::Tag do
|
|
|
226
222
|
end
|
|
227
223
|
|
|
228
224
|
context 'case sensitive' do
|
|
229
|
-
if
|
|
225
|
+
if using_case_insensitive_collation?
|
|
230
226
|
include_context 'without unique index'
|
|
231
227
|
end
|
|
232
228
|
|
|
@@ -240,7 +236,7 @@ describe ActsAsTaggableOn::Tag do
|
|
|
240
236
|
end
|
|
241
237
|
|
|
242
238
|
context 'case sensitive' do
|
|
243
|
-
if
|
|
239
|
+
if using_case_insensitive_collation?
|
|
244
240
|
include_context 'without unique index'
|
|
245
241
|
end
|
|
246
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
|
|
@@ -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
|
|
@@ -215,6 +223,20 @@ describe 'Taggable' do
|
|
|
215
223
|
expect(TaggableModel.tagged_with('ruby').to_sql).to_not match /DISTINCT/
|
|
216
224
|
end
|
|
217
225
|
|
|
226
|
+
it "should be able to find a tag using dates" do
|
|
227
|
+
@taggable.skill_list = "ruby"
|
|
228
|
+
@taggable.save
|
|
229
|
+
|
|
230
|
+
expect(TaggableModel.tagged_with("ruby", :start_at => Date.today, :end_at => Date.tomorrow).count).to eq(1)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
it "shouldn't be able to find a tag outside date range" do
|
|
234
|
+
@taggable.skill_list = "ruby"
|
|
235
|
+
@taggable.save
|
|
236
|
+
|
|
237
|
+
expect(TaggableModel.tagged_with("ruby", :start_at => Date.today - 2.days, :end_at => Date.today - 1.day).count).to eq(0)
|
|
238
|
+
end
|
|
239
|
+
|
|
218
240
|
it 'should be able to find by tag with context' do
|
|
219
241
|
@taggable.skill_list = 'ruby, rails, css'
|
|
220
242
|
@taggable.tag_list = 'bob, charlie'
|
|
@@ -255,16 +277,14 @@ describe 'Taggable' do
|
|
|
255
277
|
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
278
|
end
|
|
257
279
|
|
|
258
|
-
unless
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
TaggableModel.create(name: 'Katia', tag_list: 'ПРИВЕТ')
|
|
280
|
+
it 'should not care about case for unicode names', unless: using_sqlite? do
|
|
281
|
+
ActsAsTaggableOn.strict_case_match = false
|
|
282
|
+
TaggableModel.create(name: 'Anya', tag_list: 'ПРИВЕТ')
|
|
283
|
+
TaggableModel.create(name: 'Igor', tag_list: 'привет')
|
|
284
|
+
TaggableModel.create(name: 'Katia', tag_list: 'ПРИВЕТ')
|
|
264
285
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
end
|
|
286
|
+
expect(ActsAsTaggableOn::Tag.all.size).to eq(1)
|
|
287
|
+
expect(TaggableModel.tagged_with('привет').to_a).to eq(TaggableModel.tagged_with('ПРИВЕТ').to_a)
|
|
268
288
|
end
|
|
269
289
|
|
|
270
290
|
context 'should be able to create and find tags in languages without capitalization :' do
|
|
@@ -494,6 +514,19 @@ describe 'Taggable' do
|
|
|
494
514
|
end
|
|
495
515
|
end
|
|
496
516
|
|
|
517
|
+
it 'should options key not be deleted' do
|
|
518
|
+
options = {:exclude => true}
|
|
519
|
+
TaggableModel.tagged_with("foo", options)
|
|
520
|
+
expect(options).to eq({:exclude => true})
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
it 'should not delete tags if not updated' do
|
|
524
|
+
model = TaggableModel.create(name: 'foo', tag_list: 'ruby, rails, programming')
|
|
525
|
+
model.update_attributes(name: 'bar')
|
|
526
|
+
model.reload
|
|
527
|
+
expect(model.tag_list.sort).to eq(%w(ruby rails programming).sort)
|
|
528
|
+
end
|
|
529
|
+
|
|
497
530
|
context 'Duplicates' do
|
|
498
531
|
context 'should not create duplicate taggings' do
|
|
499
532
|
let(:bob) { TaggableModel.create(name: 'Bob') }
|
|
@@ -580,28 +613,26 @@ describe 'Taggable' do
|
|
|
580
613
|
|
|
581
614
|
end
|
|
582
615
|
|
|
583
|
-
if
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
connor.save
|
|
600
|
-
end
|
|
616
|
+
xit 'should not duplicate tags added on different threads', if: supports_concurrency?, skip: 'FIXME, Deadlocks in travis' do
|
|
617
|
+
#TODO, try with more threads and fix deadlock
|
|
618
|
+
thread_count = 4
|
|
619
|
+
barrier = Barrier.new thread_count
|
|
620
|
+
|
|
621
|
+
expect {
|
|
622
|
+
thread_count.times.map do |idx|
|
|
623
|
+
Thread.start do
|
|
624
|
+
connor = TaggableModel.first_or_create(name: 'Connor')
|
|
625
|
+
connor.tag_list = 'There, can, be, only, one'
|
|
626
|
+
barrier.wait
|
|
627
|
+
begin
|
|
628
|
+
connor.save
|
|
629
|
+
rescue ActsAsTaggableOn::DuplicateTagError
|
|
630
|
+
# second save should succeed
|
|
631
|
+
connor.save
|
|
601
632
|
end
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
|
|
633
|
+
end
|
|
634
|
+
end.map(&:join)
|
|
635
|
+
}.to change(ActsAsTaggableOn::Tag, :count).by(5)
|
|
605
636
|
end
|
|
606
637
|
end
|
|
607
638
|
|
|
@@ -621,8 +652,8 @@ describe 'Taggable' do
|
|
|
621
652
|
it 'should return all column names joined for Tag GROUP clause' do
|
|
622
653
|
# NOTE: type column supports an STI Tag subclass in the test suite, though
|
|
623
654
|
# isn't included by default in the migration generator
|
|
624
|
-
expect(@taggable.grouped_column_names_for(ActsAsTaggableOn::Tag))
|
|
625
|
-
|
|
655
|
+
expect(@taggable.grouped_column_names_for(ActsAsTaggableOn::Tag))
|
|
656
|
+
.to eq('tags.id, tags.name, tags.taggings_count, tags.type')
|
|
626
657
|
end
|
|
627
658
|
|
|
628
659
|
it 'should return all column names joined for TaggableModel GROUP clause' do
|
|
@@ -693,114 +724,6 @@ describe 'Taggable' do
|
|
|
693
724
|
end
|
|
694
725
|
end
|
|
695
726
|
|
|
696
|
-
describe 'Dirty Objects' do
|
|
697
|
-
context 'with un-contexted tags' do
|
|
698
|
-
before(:each) do
|
|
699
|
-
@taggable = TaggableModel.create(tag_list: 'awesome, epic')
|
|
700
|
-
end
|
|
701
|
-
|
|
702
|
-
context 'when tag_list changed' do
|
|
703
|
-
before(:each) do
|
|
704
|
-
expect(@taggable.changes).to be_empty
|
|
705
|
-
@taggable.tag_list = 'one'
|
|
706
|
-
end
|
|
707
|
-
|
|
708
|
-
it 'should show changes of dirty object' do
|
|
709
|
-
expect(@taggable.changes).to eq({'tag_list' => ['awesome, epic', ['one']]})
|
|
710
|
-
end
|
|
711
|
-
|
|
712
|
-
it 'flags tag_list as changed' do
|
|
713
|
-
expect(@taggable.tag_list_changed?).to be_truthy
|
|
714
|
-
end
|
|
715
|
-
|
|
716
|
-
it 'preserves original value' do
|
|
717
|
-
expect(@taggable.tag_list_was).to eq('awesome, epic')
|
|
718
|
-
end
|
|
719
|
-
|
|
720
|
-
it 'shows what the change was' do
|
|
721
|
-
expect(@taggable.tag_list_change).to eq(['awesome, epic', ['one']])
|
|
722
|
-
end
|
|
723
|
-
end
|
|
724
|
-
|
|
725
|
-
context 'when tag_list is the same' do
|
|
726
|
-
before(:each) do
|
|
727
|
-
@taggable.tag_list = 'awesome, epic'
|
|
728
|
-
end
|
|
729
|
-
|
|
730
|
-
it 'is not flagged as changed' do
|
|
731
|
-
expect(@taggable.tag_list_changed?).to be_falsy
|
|
732
|
-
end
|
|
733
|
-
|
|
734
|
-
it 'does not show any changes to the taggable item' do
|
|
735
|
-
expect(@taggable.changes).to be_empty
|
|
736
|
-
end
|
|
737
|
-
|
|
738
|
-
context "and using a delimiter different from a ','" do
|
|
739
|
-
before do
|
|
740
|
-
@old_delimiter = ActsAsTaggableOn.delimiter
|
|
741
|
-
ActsAsTaggableOn.delimiter = ';'
|
|
742
|
-
end
|
|
743
|
-
|
|
744
|
-
after do
|
|
745
|
-
ActsAsTaggableOn.delimiter = @old_delimiter
|
|
746
|
-
end
|
|
747
|
-
|
|
748
|
-
it 'does not show any changes to the taggable item when using array assignments' do
|
|
749
|
-
@taggable.tag_list = %w(awesome epic)
|
|
750
|
-
expect(@taggable.changes).to be_empty
|
|
751
|
-
end
|
|
752
|
-
end
|
|
753
|
-
end
|
|
754
|
-
end
|
|
755
|
-
|
|
756
|
-
context 'with context tags' do
|
|
757
|
-
before(:each) do
|
|
758
|
-
@taggable = TaggableModel.create('language_list' => 'awesome, epic')
|
|
759
|
-
end
|
|
760
|
-
|
|
761
|
-
context 'when language_list changed' do
|
|
762
|
-
before(:each) do
|
|
763
|
-
expect(@taggable.changes).to be_empty
|
|
764
|
-
@taggable.language_list = 'one'
|
|
765
|
-
end
|
|
766
|
-
|
|
767
|
-
it 'should show changes of dirty object' do
|
|
768
|
-
expect(@taggable.changes).to eq({'language_list' => ['awesome, epic', ['one']]})
|
|
769
|
-
end
|
|
770
|
-
|
|
771
|
-
it 'flags language_list as changed' do
|
|
772
|
-
expect(@taggable.language_list_changed?).to be_truthy
|
|
773
|
-
end
|
|
774
|
-
|
|
775
|
-
it 'preserves original value' do
|
|
776
|
-
expect(@taggable.language_list_was).to eq('awesome, epic')
|
|
777
|
-
end
|
|
778
|
-
|
|
779
|
-
it 'shows what the change was' do
|
|
780
|
-
expect(@taggable.language_list_change).to eq(['awesome, epic', ['one']])
|
|
781
|
-
end
|
|
782
|
-
|
|
783
|
-
it 'shows what the changes were' do
|
|
784
|
-
expect(@taggable.language_list_changes).to eq(['awesome, epic', ['one']])
|
|
785
|
-
end
|
|
786
|
-
end
|
|
787
|
-
|
|
788
|
-
context 'when language_list is the same' do
|
|
789
|
-
before(:each) do
|
|
790
|
-
@taggable.language_list = 'awesome, epic'
|
|
791
|
-
end
|
|
792
|
-
|
|
793
|
-
it 'is not flagged as changed' do
|
|
794
|
-
expect(@taggable.language_list_changed?).to be_falsy
|
|
795
|
-
end
|
|
796
|
-
|
|
797
|
-
it 'does not show any changes to the taggable item' do
|
|
798
|
-
expect(@taggable.changes).to be_empty
|
|
799
|
-
end
|
|
800
|
-
end
|
|
801
|
-
end
|
|
802
|
-
end
|
|
803
|
-
|
|
804
727
|
describe 'Autogenerated methods' do
|
|
805
728
|
it 'should be overridable' do
|
|
806
729
|
expect(TaggableModel.create(tag_list: 'woo').tag_list_submethod_called).to be_truthy
|
|
@@ -847,33 +770,30 @@ describe 'Taggable' do
|
|
|
847
770
|
end
|
|
848
771
|
end
|
|
849
772
|
|
|
773
|
+
describe 'Taggable model with json columns', if: postgresql_support_json? do
|
|
774
|
+
before(:each) do
|
|
775
|
+
@taggable = TaggableModelWithJson.new(:name => 'Bob Jones')
|
|
776
|
+
@taggables = [@taggable, TaggableModelWithJson.new(:name => 'John Doe')]
|
|
777
|
+
end
|
|
850
778
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
@taggables = [@taggable, TaggableModelWithJson.new(:name => 'John Doe')]
|
|
856
|
-
end
|
|
857
|
-
|
|
858
|
-
it 'should be able to find by tag with context' do
|
|
859
|
-
@taggable.skill_list = 'ruby, rails, css'
|
|
860
|
-
@taggable.tag_list = 'bob, charlie'
|
|
861
|
-
@taggable.save
|
|
779
|
+
it 'should be able to find by tag with context' do
|
|
780
|
+
@taggable.skill_list = 'ruby, rails, css'
|
|
781
|
+
@taggable.tag_list = 'bob, charlie'
|
|
782
|
+
@taggable.save
|
|
862
783
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
784
|
+
expect(TaggableModelWithJson.tagged_with('ruby').first).to eq(@taggable)
|
|
785
|
+
expect(TaggableModelWithJson.tagged_with('ruby, css').first).to eq(@taggable)
|
|
786
|
+
expect(TaggableModelWithJson.tagged_with('bob', :on => :skills).first).to_not eq(@taggable)
|
|
787
|
+
expect(TaggableModelWithJson.tagged_with('bob', :on => :tags).first).to eq(@taggable)
|
|
788
|
+
end
|
|
868
789
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
790
|
+
it 'should be able to find tagged with any tag' do
|
|
791
|
+
bob = TaggableModelWithJson.create(:name => 'Bob', :tag_list => 'fitter, happier, more productive', :skill_list => 'ruby, rails, css')
|
|
792
|
+
frank = TaggableModelWithJson.create(:name => 'Frank', :tag_list => 'weaker, depressed, inefficient', :skill_list => 'ruby, rails, css')
|
|
793
|
+
steve = TaggableModelWithJson.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
|
|
873
794
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
end
|
|
795
|
+
expect(TaggableModelWithJson.tagged_with(%w(ruby java), :order => 'taggable_model_with_jsons.name', :any => true).to_a).to eq([bob, frank, steve])
|
|
796
|
+
expect(TaggableModelWithJson.tagged_with(%w(c++ fitter), :order => 'taggable_model_with_jsons.name', :any => true).to_a).to eq([bob, steve])
|
|
797
|
+
expect(TaggableModelWithJson.tagged_with(%w(depressed css), :order => 'taggable_model_with_jsons.name', :any => true).to_a).to eq([bob, frank])
|
|
878
798
|
end
|
|
879
799
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
1
2
|
require 'spec_helper'
|
|
2
3
|
|
|
3
4
|
describe 'Tagger' do
|
|
@@ -43,7 +44,6 @@ describe 'Tagger' do
|
|
|
43
44
|
tags = TaggableModel.tagged_with(%w(ruby java), owned_by: @user, any: true)
|
|
44
45
|
expect(tags).to include(@taggable, @taggable2)
|
|
45
46
|
expect(tags.size).to eq(2)
|
|
46
|
-
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
it 'only returns objects tagged by owned_by when exclude is true' do
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
1
2
|
require 'spec_helper'
|
|
2
3
|
|
|
3
4
|
describe ActsAsTaggableOn::Tagging do
|
|
@@ -23,5 +24,40 @@ describe ActsAsTaggableOn::Tagging do
|
|
|
23
24
|
2.times { ActsAsTaggableOn::Tagging.create(taggable: @taggable, tag: @tag, context: 'tags') }
|
|
24
25
|
}).to change(ActsAsTaggableOn::Tagging, :count).by(1)
|
|
25
26
|
end
|
|
26
|
-
|
|
27
|
+
|
|
28
|
+
it 'should not delete tags of other records' do
|
|
29
|
+
6.times { TaggableModel.create(name: 'Bob Jones', tag_list: 'very, serious, bug') }
|
|
30
|
+
expect(ActsAsTaggableOn::Tag.count).to eq(3)
|
|
31
|
+
taggable = TaggableModel.first
|
|
32
|
+
taggable.tag_list = 'bug'
|
|
33
|
+
taggable.save
|
|
34
|
+
|
|
35
|
+
expect(taggable.tag_list).to eq(['bug'])
|
|
36
|
+
|
|
37
|
+
another_taggable = TaggableModel.where('id != ?', taggable.id).sample
|
|
38
|
+
expect(another_taggable.tag_list.sort).to eq(%w(very serious bug).sort)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should destroy unused tags after tagging destroyed' do
|
|
42
|
+
previous_setting = ActsAsTaggableOn.remove_unused_tags
|
|
43
|
+
ActsAsTaggableOn.remove_unused_tags = true
|
|
44
|
+
ActsAsTaggableOn::Tag.destroy_all
|
|
45
|
+
@taggable = TaggableModel.create(name: 'Bob Jones')
|
|
46
|
+
@taggable.update_attribute :tag_list, 'aaa,bbb,ccc'
|
|
47
|
+
@taggable.update_attribute :tag_list, ''
|
|
48
|
+
expect(ActsAsTaggableOn::Tag.count).to eql(0)
|
|
49
|
+
ActsAsTaggableOn.remove_unused_tags = previous_setting
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
pending 'context scopes' do
|
|
53
|
+
describe '.by_context'
|
|
54
|
+
|
|
55
|
+
describe '.by_contexts'
|
|
56
|
+
|
|
57
|
+
describe '.owned_by'
|
|
58
|
+
|
|
59
|
+
describe '.not_owned'
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
27
63
|
end
|
|
@@ -1,21 +1,16 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
1
2
|
require 'spec_helper'
|
|
2
3
|
|
|
3
4
|
describe ActsAsTaggableOn::Utils do
|
|
4
|
-
describe 'like_operator' do
|
|
5
|
-
before(:each) do
|
|
6
|
-
TaggableModel.acts_as_taggable_on(:tags, :languages, :skills, :needs, :offerings)
|
|
7
|
-
@taggable = TaggableModel.new(name: 'Bob Jones')
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
describe '#like_operator' do
|
|
11
6
|
it 'should return \'ILIKE\' when the adapter is PostgreSQL' do
|
|
12
|
-
allow(
|
|
13
|
-
expect(
|
|
7
|
+
allow(ActsAsTaggableOn::Utils.connection).to receive(:adapter_name) { 'PostgreSQL' }
|
|
8
|
+
expect(ActsAsTaggableOn::Utils.like_operator).to eq('ILIKE')
|
|
14
9
|
end
|
|
15
10
|
|
|
16
11
|
it 'should return \'LIKE\' when the adapter is not PostgreSQL' do
|
|
17
|
-
allow(
|
|
18
|
-
expect(
|
|
12
|
+
allow(ActsAsTaggableOn::Utils.connection).to receive(:adapter_name) { 'MySQL' }
|
|
13
|
+
expect(ActsAsTaggableOn::Utils.like_operator).to eq('LIKE')
|
|
19
14
|
end
|
|
20
15
|
end
|
|
21
16
|
end
|