acts-as-taggable-on 3.5.0 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/spec.yml +95 -0
  3. data/.gitignore +2 -0
  4. data/Appraisals +12 -9
  5. data/CHANGELOG.md +212 -70
  6. data/CONTRIBUTING.md +13 -0
  7. data/Gemfile +2 -1
  8. data/README.md +128 -25
  9. data/acts-as-taggable-on.gemspec +2 -6
  10. data/db/migrate/1_acts_as_taggable_on_migration.rb +14 -8
  11. data/db/migrate/2_add_missing_unique_indices.rb +15 -9
  12. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +9 -4
  13. data/db/migrate/4_add_missing_taggable_index.rb +8 -3
  14. data/db/migrate/5_change_collation_for_tag_names.rb +7 -2
  15. data/db/migrate/6_add_missing_indexes_on_taggings.rb +22 -0
  16. data/db/migrate/7_add_tenant_to_taggings.rb +16 -0
  17. data/gemfiles/activerecord_5.0.gemfile +21 -0
  18. data/gemfiles/activerecord_5.1.gemfile +21 -0
  19. data/gemfiles/activerecord_5.2.gemfile +21 -0
  20. data/gemfiles/activerecord_6.0.gemfile +21 -0
  21. data/gemfiles/activerecord_6.1.gemfile +23 -0
  22. data/lib/acts-as-taggable-on.rb +22 -14
  23. data/lib/acts_as_taggable_on/engine.rb +0 -1
  24. data/lib/acts_as_taggable_on/tag.rb +27 -23
  25. data/lib/acts_as_taggable_on/tag_list.rb +3 -13
  26. data/lib/acts_as_taggable_on/taggable/cache.rb +39 -35
  27. data/lib/acts_as_taggable_on/taggable/collection.rb +14 -9
  28. data/lib/acts_as_taggable_on/taggable/core.rb +59 -184
  29. data/lib/acts_as_taggable_on/taggable/ownership.rb +19 -8
  30. data/lib/acts_as_taggable_on/taggable/related.rb +1 -1
  31. data/lib/acts_as_taggable_on/taggable/tag_list_type.rb +4 -0
  32. data/lib/acts_as_taggable_on/taggable/tagged_with_query/all_tags_query.rb +111 -0
  33. data/lib/acts_as_taggable_on/taggable/tagged_with_query/any_tags_query.rb +70 -0
  34. data/lib/acts_as_taggable_on/taggable/tagged_with_query/exclude_tags_query.rb +82 -0
  35. data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +61 -0
  36. data/lib/acts_as_taggable_on/taggable/tagged_with_query.rb +16 -0
  37. data/lib/acts_as_taggable_on/taggable.rb +18 -1
  38. data/lib/acts_as_taggable_on/tagger.rb +12 -11
  39. data/lib/acts_as_taggable_on/tagging.rb +9 -14
  40. data/lib/acts_as_taggable_on/utils.rb +4 -5
  41. data/lib/acts_as_taggable_on/version.rb +1 -2
  42. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +14 -13
  43. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -1
  44. data/spec/acts_as_taggable_on/caching_spec.rb +55 -9
  45. data/spec/acts_as_taggable_on/{taggable/dirty_spec.rb → dirty_spec.rb} +28 -13
  46. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +28 -8
  47. data/spec/acts_as_taggable_on/tag_list_spec.rb +27 -1
  48. data/spec/acts_as_taggable_on/tag_spec.rb +31 -1
  49. data/spec/acts_as_taggable_on/taggable_spec.rb +40 -19
  50. data/spec/acts_as_taggable_on/tagger_spec.rb +2 -2
  51. data/spec/acts_as_taggable_on/tagging_spec.rb +87 -7
  52. data/spec/internal/app/models/altered_inheriting_taggable_model.rb +2 -0
  53. data/spec/internal/app/models/cached_model_with_array.rb +6 -0
  54. data/spec/internal/app/models/columns_override_model.rb +5 -0
  55. data/spec/internal/app/models/company.rb +1 -1
  56. data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
  57. data/spec/internal/app/models/market.rb +1 -1
  58. data/spec/internal/app/models/non_standard_id_taggable_model.rb +1 -1
  59. data/spec/internal/app/models/student.rb +2 -0
  60. data/spec/internal/app/models/taggable_model.rb +3 -0
  61. data/spec/internal/app/models/user.rb +1 -1
  62. data/spec/internal/config/database.yml.sample +4 -8
  63. data/spec/internal/db/schema.rb +23 -7
  64. data/spec/spec_helper.rb +0 -1
  65. data/spec/support/database.rb +5 -11
  66. metadata +27 -75
  67. data/.travis.yml +0 -40
  68. data/UPGRADING.md +0 -8
  69. data/gemfiles/activerecord_3.2.gemfile +0 -15
  70. data/gemfiles/activerecord_4.0.gemfile +0 -15
  71. data/gemfiles/activerecord_4.1.gemfile +0 -15
  72. data/gemfiles/activerecord_4.2.gemfile +0 -16
  73. data/lib/acts_as_taggable_on/compatibility.rb +0 -35
  74. data/lib/acts_as_taggable_on/tag_list_parser.rb +0 -21
  75. data/lib/acts_as_taggable_on/taggable/dirty.rb +0 -36
  76. data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +0 -46
  77. data/spec/internal/app/models/models.rb +0 -90
@@ -31,40 +31,40 @@ describe 'Acts As Taggable On' do
31
31
  end
32
32
 
33
33
  it 'should cache tags' do
34
- @taggable.update_attributes(tag_list: 'awesome, epic')
34
+ @taggable.update(tag_list: 'awesome, epic')
35
35
  expect(@taggable.cached_tag_list).to eq('awesome, epic')
36
36
 
37
- @another_taggable.update_attributes(language_list: 'ruby, .net')
37
+ @another_taggable.update(language_list: 'ruby, .net')
38
38
  expect(@another_taggable.cached_language_list).to eq('ruby, .net')
39
39
  end
40
40
 
41
41
  it 'should keep the cache' do
42
- @taggable.update_attributes(tag_list: 'awesome, epic')
42
+ @taggable.update(tag_list: 'awesome, epic')
43
43
  @taggable = CachedModel.find(@taggable.id)
44
44
  @taggable.save!
45
45
  expect(@taggable.cached_tag_list).to eq('awesome, epic')
46
46
  end
47
47
 
48
48
  it 'should update the cache' do
49
- @taggable.update_attributes(tag_list: 'awesome, epic')
50
- @taggable.update_attributes(tag_list: 'awesome')
49
+ @taggable.update(tag_list: 'awesome, epic')
50
+ @taggable.update(tag_list: 'awesome')
51
51
  expect(@taggable.cached_tag_list).to eq('awesome')
52
52
  end
53
53
 
54
54
  it 'should remove the cache' do
55
- @taggable.update_attributes(tag_list: 'awesome, epic')
56
- @taggable.update_attributes(tag_list: '')
55
+ @taggable.update(tag_list: 'awesome, epic')
56
+ @taggable.update(tag_list: '')
57
57
  expect(@taggable.cached_tag_list).to be_blank
58
58
  end
59
59
 
60
60
  it 'should have a tag list' do
61
- @taggable.update_attributes(tag_list: 'awesome, epic')
61
+ @taggable.update(tag_list: 'awesome, epic')
62
62
  @taggable = CachedModel.find(@taggable.id)
63
63
  expect(@taggable.tag_list.sort).to eq(%w(awesome epic).sort)
64
64
  end
65
65
 
66
66
  it 'should keep the tag list' do
67
- @taggable.update_attributes(tag_list: 'awesome, epic')
67
+ @taggable.update(tag_list: 'awesome, epic')
68
68
  @taggable = CachedModel.find(@taggable.id)
69
69
  @taggable.save!
70
70
  expect(@taggable.tag_list.sort).to eq(%w(awesome epic).sort)
@@ -75,6 +75,52 @@ describe 'Acts As Taggable On' do
75
75
  CachedModel.reset_column_information
76
76
  expect(CachedModel.instance_variable_get(:@acts_as_taggable_on_cache_columns)).to eql(nil)
77
77
  end
78
+
79
+ it 'should not override a user-defined columns method' do
80
+ expect(ColumnsOverrideModel.columns.map(&:name)).not_to include('ignored_column')
81
+ ColumnsOverrideModel.acts_as_taggable
82
+ expect(ColumnsOverrideModel.columns.map(&:name)).not_to include('ignored_column')
83
+ end
84
+ end
85
+
86
+ describe 'with a custom delimiter' do
87
+ before(:each) do
88
+ @taggable = CachedModel.new(name: 'Bob Jones')
89
+ @another_taggable = OtherCachedModel.new(name: 'John Smith')
90
+ ActsAsTaggableOn.delimiter = ';'
91
+ end
92
+
93
+ after(:all) do
94
+ ActsAsTaggableOn.delimiter = ','
95
+ end
96
+
97
+ it 'should cache tags with custom delimiter' do
98
+ @taggable.update(tag_list: 'awesome; epic')
99
+ expect(@taggable.tag_list).to eq(['awesome', 'epic'])
100
+ expect(@taggable.cached_tag_list).to eq('awesome; epic')
101
+
102
+ @taggable = CachedModel.find_by_name('Bob Jones')
103
+ expect(@taggable.tag_list).to eq(['awesome', 'epic'])
104
+ expect(@taggable.cached_tag_list).to eq('awesome; epic')
105
+ end
106
+ end
107
+
108
+ describe 'Cache methods initialization on new models' do
109
+ before(:all) do
110
+ ActiveRecord::Base.connection.execute(
111
+ 'INSERT INTO cache_methods_injected_models (cached_tag_list) VALUES (\'ciao\')'
112
+ )
113
+ class CacheMethodsInjectedModel < ActiveRecord::Base
114
+ acts_as_taggable
115
+ end
116
+ end
117
+ after(:all) { Object.send(:remove_const, :CacheMethodsInjectedModel) }
118
+
119
+ it 'cached_tag_list_on? get injected correctly' do
120
+ expect do
121
+ CacheMethodsInjectedModel.first.tag_list
122
+ end.not_to raise_error
123
+ end
78
124
  end
79
125
 
80
126
  describe 'CachingWithArray' do
@@ -1,7 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'spec_helper'
3
3
 
4
- describe ActsAsTaggableOn::Taggable::Dirty do
4
+ describe 'Dirty behavior of taggable objects' do
5
5
  context 'with un-contexted tags' do
6
6
  before(:each) do
7
7
  @taggable = TaggableModel.create(tag_list: 'awesome, epic')
@@ -14,19 +14,27 @@ describe ActsAsTaggableOn::Taggable::Dirty do
14
14
  end
15
15
 
16
16
  it 'should show changes of dirty object' do
17
- expect(@taggable.changes).to eq({'tag_list' => ['awesome, epic', ['one']]})
17
+ expect(@taggable.changes).to eq({'tag_list' => [['awesome', 'epic'], ['one']]})
18
18
  end
19
19
 
20
- it 'flags tag_list as changed' do
21
- expect(@taggable.tag_list_changed?).to be_truthy
20
+ it 'should show changes of freshly initialized dirty object' do
21
+ taggable = TaggableModel.find(@taggable.id)
22
+ taggable.tag_list = 'one'
23
+ expect(taggable.changes).to eq({'tag_list' => [['awesome', 'epic'], ['one']]})
24
+ end
25
+
26
+ if Rails.version >= "5.1"
27
+ it 'flags tag_list as changed' do
28
+ expect(@taggable.will_save_change_to_tag_list?).to be_truthy
29
+ end
22
30
  end
23
31
 
24
32
  it 'preserves original value' do
25
- expect(@taggable.tag_list_was).to eq('awesome, epic')
33
+ expect(@taggable.tag_list_was).to eq(['awesome', 'epic'])
26
34
  end
27
35
 
28
36
  it 'shows what the change was' do
29
- expect(@taggable.tag_list_change).to eq(['awesome, epic', ['one']])
37
+ expect(@taggable.tag_list_change).to eq([['awesome', 'epic'], ['one']])
30
38
  end
31
39
 
32
40
  context 'without order' do
@@ -90,7 +98,7 @@ describe ActsAsTaggableOn::Taggable::Dirty do
90
98
  end
91
99
 
92
100
  it 'should show changes of dirty object' do
93
- expect(@taggable.changes).to eq({'language_list' => ['awesome, epic', ['one']]})
101
+ expect(@taggable.changes).to eq({'language_list' => [['awesome', 'epic'], ['one']]})
94
102
  end
95
103
 
96
104
  it 'flags language_list as changed' do
@@ -98,15 +106,11 @@ describe ActsAsTaggableOn::Taggable::Dirty do
98
106
  end
99
107
 
100
108
  it 'preserves original value' do
101
- expect(@taggable.language_list_was).to eq('awesome, epic')
109
+ expect(@taggable.language_list_was).to eq(['awesome', 'epic'])
102
110
  end
103
111
 
104
112
  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']])
113
+ expect(@taggable.language_list_change).to eq([['awesome', 'epic'], ['one']])
110
114
  end
111
115
  end
112
116
 
@@ -123,5 +127,16 @@ describe ActsAsTaggableOn::Taggable::Dirty do
123
127
  expect(@taggable.changes).to be_empty
124
128
  end
125
129
  end
130
+
131
+ context 'when language_list changed by association' do
132
+ let(:tag) { ActsAsTaggableOn::Tag.new(name: 'one') }
133
+
134
+ it 'flags language_list as changed' do
135
+ expect(@taggable.changes).to be_empty
136
+ @taggable.languages << tag
137
+ expect(@taggable.language_list_changed?).to be_truthy
138
+ end
139
+ end
140
+
126
141
  end
127
142
  end
@@ -127,9 +127,9 @@ describe 'Single Table Inheritance' do
127
127
  altered_inheriting.tag_list = 'fork, spoon'
128
128
  altered_inheriting.save!
129
129
 
130
- expect(InheritingTaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso))
131
- expect(AlteredInheritingTaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(fork spoon))
132
- expect(TaggableModel.tag_counts_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso fork spoon))
130
+ expect(InheritingTaggableModel.tag_counts_on(:tags, order: "#{ActsAsTaggableOn.tags_table}.id").map(&:name)).to eq(%w(bob kelso))
131
+ expect(AlteredInheritingTaggableModel.tag_counts_on(:tags, order: "#{ActsAsTaggableOn.tags_table}.id").map(&:name)).to eq(%w(fork spoon))
132
+ expect(TaggableModel.tag_counts_on(:tags, order: "#{ActsAsTaggableOn.tags_table}.id").map(&:name)).to eq(%w(bob kelso fork spoon))
133
133
  end
134
134
 
135
135
  it 'should have different tags_on for inherited models' do
@@ -138,9 +138,9 @@ describe 'Single Table Inheritance' do
138
138
  altered_inheriting.tag_list = 'fork, spoon'
139
139
  altered_inheriting.save!
140
140
 
141
- expect(InheritingTaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso))
142
- expect(AlteredInheritingTaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(fork spoon))
143
- expect(TaggableModel.tags_on(:tags, order: 'tags.id').map(&:name)).to eq(%w(bob kelso fork spoon))
141
+ expect(InheritingTaggableModel.tags_on(:tags, order: "#{ActsAsTaggableOn.tags_table}.id").map(&:name)).to eq(%w(bob kelso))
142
+ expect(AlteredInheritingTaggableModel.tags_on(:tags, order: "#{ActsAsTaggableOn.tags_table}.id").map(&:name)).to eq(%w(fork spoon))
143
+ expect(TaggableModel.tags_on(:tags, order: "#{ActsAsTaggableOn.tags_table}.id").map(&:name)).to eq(%w(bob kelso fork spoon))
144
144
  end
145
145
 
146
146
  it 'should store same tag without validation conflict' do
@@ -150,7 +150,12 @@ describe 'Single Table Inheritance' do
150
150
  inheriting_model.tag_list = 'one'
151
151
  inheriting_model.save!
152
152
 
153
- inheriting_model.update_attributes! name: 'foo'
153
+ inheriting_model.update! name: 'foo'
154
+ end
155
+
156
+ it "should only join with taggable's table to check type for inherited models" do
157
+ expect(TaggableModel.tag_counts_on(:tags).to_sql).to_not match /INNER JOIN taggable_models ON/
158
+ expect(InheritingTaggableModel.tag_counts_on(:tags).to_sql).to match /INNER JOIN taggable_models ON/
154
159
  end
155
160
  end
156
161
 
@@ -170,11 +175,27 @@ describe 'Single Table Inheritance' do
170
175
  expect(taggable.tags_from(student)).to eq(%w(ruby scheme))
171
176
  end
172
177
 
178
+ it 'returns all owner tags on the taggable' do
179
+ student.tag(taggable, with: 'ruby, scheme', on: :tags)
180
+ student.tag(taggable, with: 'skill_one', on: :skills)
181
+ student.tag(taggable, with: 'english, spanish', on: :language)
182
+ expect(taggable.owner_tags(student).count).to eq(5)
183
+ expect(taggable.owner_tags(student).sort == %w(english ruby scheme skill_one spanish))
184
+ end
185
+
186
+
173
187
  it 'returns owner tags on the tagger' do
174
188
  student.tag(taggable, with: 'ruby, scheme', on: :tags)
175
189
  expect(taggable.owner_tags_on(student, :tags).count).to eq(2)
176
190
  end
177
191
 
192
+ it 'returns owner tags on the taggable for an array of contexts' do
193
+ student.tag(taggable, with: 'ruby, scheme', on: :tags)
194
+ student.tag(taggable, with: 'skill_one, skill_two', on: :skills)
195
+ expect(taggable.owner_tags_on(student, [:tags, :skills]).count).to eq(4)
196
+ expect(taggable.owner_tags_on(student, [:tags, :skills]).sort == %w(ruby scheme skill_one skill_two))
197
+ end
198
+
178
199
  it 'should scope objects returned by tagged_with by owners' do
179
200
  student.tag(taggable, with: 'ruby, scheme', on: :tags)
180
201
  expect(TaggableModel.tagged_with(%w(ruby scheme), owned_by: student).count).to eq(1)
@@ -208,4 +229,3 @@ describe 'Single Table Inheritance' do
208
229
  end
209
230
  end
210
231
  end
211
-
@@ -83,6 +83,18 @@ describe ActsAsTaggableOn::TagList do
83
83
  new_tag_list = tag_list.concat(another_tag_list)
84
84
  expect(new_tag_list.class).to eq(ActsAsTaggableOn::TagList)
85
85
  end
86
+
87
+ context 'without duplicates' do
88
+ let(:arr) { ['crazy', 'alien'] }
89
+ let(:another_tag_list) { ActsAsTaggableOn::TagList.new(*arr) }
90
+ it 'adds other list' do
91
+ expect(tag_list.concat(another_tag_list)).to eq(%w[awesome radical crazy alien])
92
+ end
93
+
94
+ it 'adds other array' do
95
+ expect(tag_list.concat(arr)).to eq(%w[awesome radical crazy alien])
96
+ end
97
+ end
86
98
  end
87
99
 
88
100
  describe '#to_s' do
@@ -92,7 +104,7 @@ describe ActsAsTaggableOn::TagList do
92
104
 
93
105
  it 'should be able to call to_s on a frozen tag list' do
94
106
  tag_list.freeze
95
- expect(-> { tag_list.add('cool', 'rad,bodacious') }).to raise_error
107
+ expect(-> { tag_list.add('cool', 'rad,bodacious') }).to raise_error(RuntimeError)
96
108
  expect(-> { tag_list.to_s }).to_not raise_error
97
109
  end
98
110
  end
@@ -114,6 +126,20 @@ describe ActsAsTaggableOn::TagList do
114
126
 
115
127
  ActsAsTaggableOn.force_lowercase = false
116
128
  end
129
+
130
+ it 'should ignore case when removing duplicates if strict_case_match is false' do
131
+ tag_list = ActsAsTaggableOn::TagList.new('Junglist', 'JUNGLIST', 'Junglist', 'Massive', 'MASSIVE', 'MASSIVE')
132
+
133
+ expect(tag_list.to_s).to eq('Junglist, Massive')
134
+ end
135
+
136
+ it 'should not ignore case when removing duplicates if strict_case_match is true' do
137
+ ActsAsTaggableOn.strict_case_match = true
138
+ tag_list = ActsAsTaggableOn::TagList.new('Junglist', 'JUNGLIST', 'Junglist', 'Massive', 'MASSIVE', 'MASSIVE')
139
+
140
+ expect(tag_list.to_s).to eq('Junglist, JUNGLIST, Massive, MASSIVE')
141
+ ActsAsTaggableOn.strict_case_match = false
142
+ end
117
143
  end
118
144
 
119
145
  describe 'custom parser' do
@@ -14,7 +14,7 @@ end
14
14
  describe ActsAsTaggableOn::Tag do
15
15
  before(:each) do
16
16
  @tag = ActsAsTaggableOn::Tag.new
17
- @user = TaggableModel.create(name: 'Pablo')
17
+ @user = TaggableModel.create(name: 'Pablo', tenant_id: 100)
18
18
  end
19
19
 
20
20
 
@@ -55,6 +55,36 @@ describe ActsAsTaggableOn::Tag do
55
55
  end
56
56
  end
57
57
 
58
+ describe 'for context' do
59
+ before(:each) do
60
+ @user.skill_list.add('ruby')
61
+ @user.save
62
+ end
63
+
64
+ it 'should return tags that have been used in the given context' do
65
+ expect(ActsAsTaggableOn::Tag.for_context('skills').pluck(:name)).to include('ruby')
66
+ end
67
+
68
+ it 'should not return tags that have been used in other contexts' do
69
+ expect(ActsAsTaggableOn::Tag.for_context('needs').pluck(:name)).to_not include('ruby')
70
+ end
71
+ end
72
+
73
+ describe 'for tenant' do
74
+ before(:each) do
75
+ @user.skill_list.add('ruby')
76
+ @user.save
77
+ end
78
+
79
+ it 'should return tags for the tenant' do
80
+ expect(ActsAsTaggableOn::Tag.for_tenant('100').pluck(:name)).to include('ruby')
81
+ end
82
+
83
+ it 'should not return tags for other tenants' do
84
+ expect(ActsAsTaggableOn::Tag.for_tenant('200').pluck(:name)).to_not include('ruby')
85
+ end
86
+ end
87
+
58
88
  describe 'find or create by name' do
59
89
  before(:each) do
60
90
  @tag.name = 'awesome'
@@ -109,6 +109,10 @@ describe 'Taggable' do
109
109
  expect(@taggable.tag_types).to eq(TaggableModel.tag_types)
110
110
  end
111
111
 
112
+ it 'should have tenant column' do
113
+ expect(TaggableModel.tenant_column).to eq(:tenant_id)
114
+ end
115
+
112
116
  it 'should have tag_counts_on' do
113
117
  expect(TaggableModel.tag_counts_on(:tags)).to be_empty
114
118
 
@@ -119,6 +123,21 @@ describe 'Taggable' do
119
123
  expect(@taggable.tag_counts_on(:tags).length).to eq(2)
120
124
  end
121
125
 
126
+ context 'tag_counts on a collection' do
127
+ context 'a select clause is specified on the collection' do
128
+ it 'should return tag counts without raising an error' do
129
+ expect(TaggableModel.tag_counts_on(:tags)).to be_empty
130
+
131
+ @taggable.tag_list = %w(awesome epic)
132
+ @taggable.save
133
+
134
+ expect {
135
+ expect(TaggableModel.select(:name).tag_counts_on(:tags).length).to eq(2)
136
+ }.not_to raise_error
137
+ end
138
+ end
139
+ end
140
+
122
141
  it 'should have tags_on' do
123
142
  expect(TaggableModel.tags_on(:tags)).to be_empty
124
143
 
@@ -185,8 +204,8 @@ describe 'Taggable' do
185
204
  @taggables[1].skill_list = 'css'
186
205
  @taggables.each { |taggable| taggable.save }
187
206
 
188
- @found_taggables_by_tag = TaggableModel.joins(:tags).where(tags: {name: ['bob']})
189
- @found_taggables_by_skill = TaggableModel.joins(:skills).where(tags: {name: ['ruby']})
207
+ @found_taggables_by_tag = TaggableModel.joins(:tags).where(ActsAsTaggableOn.tags_table => {name: ['bob']})
208
+ @found_taggables_by_skill = TaggableModel.joins(:skills).where(ActsAsTaggableOn.tags_table => {name: ['ruby']})
190
209
 
191
210
  expect(@found_taggables_by_tag).to include @taggables[0]
192
211
  expect(@found_taggables_by_tag).to_not include @taggables[1]
@@ -226,11 +245,13 @@ describe 'Taggable' do
226
245
  it "should be able to find a tag using dates" do
227
246
  @taggable.skill_list = "ruby"
228
247
  @taggable.save
248
+ today = Date.today.to_time.utc
249
+ tomorrow = Date.tomorrow.to_time.utc
229
250
 
230
- expect(TaggableModel.tagged_with("ruby", :start_at => Date.today, :end_at => Date.tomorrow).count).to eq(1)
251
+ expect(TaggableModel.tagged_with("ruby", :start_at => today, :end_at => tomorrow).count).to eq(1)
231
252
  end
232
253
 
233
- it "shouldn't be able to find a tag outside date range" do
254
+ it "shouldn't be able to find a tag outside date range" do
234
255
  @taggable.skill_list = "ruby"
235
256
  @taggable.save
236
257
 
@@ -238,14 +259,17 @@ describe 'Taggable' do
238
259
  end
239
260
 
240
261
  it 'should be able to find by tag with context' do
241
- @taggable.skill_list = 'ruby, rails, css'
242
- @taggable.tag_list = 'bob, charlie'
262
+ @taggable.skill_list = 'ruby, rails, css, julia'
263
+ @taggable.tag_list = 'bob, charlie, julia'
243
264
  @taggable.save
244
265
 
245
266
  expect(TaggableModel.tagged_with('ruby').first).to eq(@taggable)
246
267
  expect(TaggableModel.tagged_with('ruby, css').first).to eq(@taggable)
247
268
  expect(TaggableModel.tagged_with('bob', on: :skills).first).to_not eq(@taggable)
248
269
  expect(TaggableModel.tagged_with('bob', on: :tags).first).to eq(@taggable)
270
+ expect(TaggableModel.tagged_with('julia', on: :skills).size).to eq(1)
271
+ expect(TaggableModel.tagged_with('julia', on: :tags).size).to eq(1)
272
+ expect(TaggableModel.tagged_with('julia', on: nil).size).to eq(2)
249
273
  end
250
274
 
251
275
  it 'should not care about case' do
@@ -318,7 +342,7 @@ describe 'Taggable' do
318
342
  TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
319
343
 
320
344
  expect(TaggableModel.all_tag_counts).to_not be_empty
321
- expect(TaggableModel.all_tag_counts(order: 'tags.id').first.count).to eq(3) # ruby
345
+ expect(TaggableModel.all_tag_counts(order: "#{ActsAsTaggableOn.tags_table}.id").first.count).to eq(3) # ruby
322
346
  end
323
347
 
324
348
  it 'should be able to get all tags on model as whole' do
@@ -327,7 +351,7 @@ describe 'Taggable' do
327
351
  TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
328
352
 
329
353
  expect(TaggableModel.all_tags).to_not be_empty
330
- expect(TaggableModel.all_tags(order: 'tags.id').first.name).to eq('ruby')
354
+ expect(TaggableModel.all_tags(order: "#{ActsAsTaggableOn.tags_table}.id").first.name).to eq('ruby')
331
355
  end
332
356
 
333
357
  it 'should be able to use named scopes to chain tag finds by any tags by context' do
@@ -349,7 +373,7 @@ describe 'Taggable' do
349
373
  TaggableModel.create(name: 'Frank', tag_list: 'ruby, rails')
350
374
  TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
351
375
 
352
- expect(TaggableModel.tagged_with('ruby').tag_counts(order: 'tags.id').first.count).to eq(2) # ruby
376
+ expect(TaggableModel.tagged_with('ruby').tag_counts(order: "#{ActsAsTaggableOn.tags_table}.id").first.count).to eq(2) # ruby
353
377
  expect(TaggableModel.tagged_with('ruby').skill_counts.first.count).to eq(1) # ruby
354
378
  end
355
379
 
@@ -358,7 +382,7 @@ describe 'Taggable' do
358
382
  TaggableModel.create(name: 'Frank', tag_list: 'ruby, rails')
359
383
  TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
360
384
 
361
- expect(TaggableModel.tagged_with('ruby').all_tag_counts(order: 'tags.id').first.count).to eq(3) # ruby
385
+ expect(TaggableModel.tagged_with('ruby').all_tag_counts(order: "#{ActsAsTaggableOn.tags_table}.id").first.count).to eq(3) # ruby
362
386
  end
363
387
 
364
388
  it 'should be able to get all scoped tags' do
@@ -366,7 +390,7 @@ describe 'Taggable' do
366
390
  TaggableModel.create(name: 'Frank', tag_list: 'ruby, rails')
367
391
  TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
368
392
 
369
- expect(TaggableModel.tagged_with('ruby').all_tags(order: 'tags.id').first.name).to eq('ruby')
393
+ expect(TaggableModel.tagged_with('ruby').all_tags(order: "#{ActsAsTaggableOn.tags_table}.id").first.name).to eq('ruby')
370
394
  end
371
395
 
372
396
  it 'should only return tag counts for the available scope' do
@@ -380,7 +404,6 @@ describe 'Taggable' do
380
404
  # Test specific join syntaxes:
381
405
  frank.untaggable_models.create!
382
406
  expect(TaggableModel.tagged_with('rails').joins(:untaggable_models).all_tag_counts.size).to eq(2)
383
- expect(TaggableModel.tagged_with('rails').joins(untaggable_models: :taggable_model).all_tag_counts.size).to eq(2)
384
407
  expect(TaggableModel.tagged_with('rails').joins([:untaggable_models]).all_tag_counts.size).to eq(2)
385
408
  end
386
409
 
@@ -395,7 +418,6 @@ describe 'Taggable' do
395
418
  # Test specific join syntaxes:
396
419
  frank.untaggable_models.create!
397
420
  expect(TaggableModel.tagged_with('rails').joins(:untaggable_models).all_tags.size).to eq(2)
398
- expect(TaggableModel.tagged_with('rails').joins(untaggable_models: :taggable_model).all_tags.size).to eq(2)
399
421
  expect(TaggableModel.tagged_with('rails').joins([:untaggable_models]).all_tags.size).to eq(2)
400
422
  end
401
423
 
@@ -459,7 +481,7 @@ describe 'Taggable' do
459
481
 
460
482
  expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, any: true).to_a.sort_by { |o| o.id }).to eq([bob, frank, steve])
461
483
  expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, exclude: true).to_a).to eq([jim])
462
- expect(TaggableModel.tagged_with('ji', wild: true, any: true).to_a).to eq([frank, jim])
484
+ expect(TaggableModel.tagged_with('ji', wild: true, any: true).to_a).to match_array([frank, jim])
463
485
  end
464
486
  end
465
487
 
@@ -523,7 +545,7 @@ describe 'Taggable' do
523
545
 
524
546
  it 'should not delete tags if not updated' do
525
547
  model = TaggableModel.create(name: 'foo', tag_list: 'ruby, rails, programming')
526
- model.update_attributes(name: 'bar')
548
+ model.update(name: 'bar')
527
549
  model.reload
528
550
  expect(model.tag_list.sort).to eq(%w(ruby rails programming).sort)
529
551
  end
@@ -654,15 +676,15 @@ describe 'Taggable' do
654
676
  # NOTE: type column supports an STI Tag subclass in the test suite, though
655
677
  # isn't included by default in the migration generator
656
678
  expect(@taggable.grouped_column_names_for(ActsAsTaggableOn::Tag))
657
- .to eq('tags.id, tags.name, tags.taggings_count, tags.type')
679
+ .to eq("#{ActsAsTaggableOn.tags_table}.id, #{ActsAsTaggableOn.tags_table}.name, #{ActsAsTaggableOn.tags_table}.taggings_count, #{ActsAsTaggableOn.tags_table}.type")
658
680
  end
659
681
 
660
682
  it 'should return all column names joined for TaggableModel GROUP clause' do
661
- expect(@taggable.grouped_column_names_for(TaggableModel)).to eq('taggable_models.id, taggable_models.name, taggable_models.type')
683
+ expect(@taggable.grouped_column_names_for(TaggableModel)).to eq('taggable_models.id, taggable_models.name, taggable_models.type, taggable_models.tenant_id')
662
684
  end
663
685
 
664
686
  it 'should return all column names joined for NonStandardIdTaggableModel GROUP clause' do
665
- expect(@taggable.grouped_column_names_for(TaggableModel)).to eq("taggable_models.#{TaggableModel.primary_key}, taggable_models.name, taggable_models.type")
687
+ expect(@taggable.grouped_column_names_for(TaggableModel)).to eq("taggable_models.#{TaggableModel.primary_key}, taggable_models.name, taggable_models.type, taggable_models.tenant_id")
666
688
  end
667
689
  end
668
690
 
@@ -743,7 +765,6 @@ describe 'Taggable' do
743
765
 
744
766
  context 'Model.limit(x).tag_counts.sum(:tags_count)' do
745
767
  it 'should not break on Mysql' do
746
- # Activerecord 3.2 return a string
747
768
  expect(TaggableModel.limit(2).tag_counts.sum('tags_count').to_i).to eq(5)
748
769
  end
749
770
  end
@@ -112,7 +112,7 @@ describe 'Tagger' do
112
112
  end
113
113
 
114
114
  it 'should not lose tags' do
115
- @taggable.update_attributes(tag_list: 'ruby')
115
+ @taggable.update(tag_list: 'ruby')
116
116
  @user.tag(@taggable, with: 'ruby, scheme', on: :tags)
117
117
 
118
118
  [@taggable, @user].each(&:reload)
@@ -120,7 +120,7 @@ describe 'Tagger' do
120
120
  expect(@taggable.all_tags_list.sort).to eq(%w(ruby scheme).sort)
121
121
 
122
122
  expect(-> {
123
- @taggable.update_attributes(tag_list: '')
123
+ @taggable.update(tag_list: '')
124
124
  }).to change(ActsAsTaggableOn::Tagging, :count).by(-1)
125
125
 
126
126
  expect(@taggable.tag_list).to be_empty
@@ -49,15 +49,95 @@ describe ActsAsTaggableOn::Tagging do
49
49
  ActsAsTaggableOn.remove_unused_tags = previous_setting
50
50
  end
51
51
 
52
- pending 'context scopes' do
53
- describe '.by_context'
54
-
55
- describe '.by_contexts'
56
-
57
- describe '.owned_by'
52
+ it 'should destroy unused tags after tagging destroyed when not using tags_counter' do
53
+ remove_unused_tags_previous_setting = ActsAsTaggableOn.remove_unused_tags
54
+ tags_counter_previous_setting = ActsAsTaggableOn.tags_counter
55
+ ActsAsTaggableOn.remove_unused_tags = true
56
+ ActsAsTaggableOn.tags_counter = false
58
57
 
59
- describe '.not_owned'
58
+ ActsAsTaggableOn::Tag.destroy_all
59
+ @taggable = TaggableModel.create(name: 'Bob Jones')
60
+ @taggable.update_attribute :tag_list, 'aaa,bbb,ccc'
61
+ @taggable.update_attribute :tag_list, ''
62
+ expect(ActsAsTaggableOn::Tag.count).to eql(0)
60
63
 
64
+ ActsAsTaggableOn.remove_unused_tags = remove_unused_tags_previous_setting
65
+ ActsAsTaggableOn.tags_counter = tags_counter_previous_setting
61
66
  end
62
67
 
68
+ describe 'context scopes' do
69
+ before do
70
+ @tagging_2 = ActsAsTaggableOn::Tagging.new
71
+ @tagging_3 = ActsAsTaggableOn::Tagging.new
72
+
73
+ @tagger = User.new
74
+ @tagger_2 = User.new
75
+
76
+ @tagging.taggable = TaggableModel.create(name: "Black holes")
77
+ @tagging.tag = ActsAsTaggableOn::Tag.create(name: "Physics")
78
+ @tagging.tagger = @tagger
79
+ @tagging.context = 'Science'
80
+ @tagging.tenant = 'account1'
81
+ @tagging.save
82
+
83
+ @tagging_2.taggable = TaggableModel.create(name: "Satellites")
84
+ @tagging_2.tag = ActsAsTaggableOn::Tag.create(name: "Technology")
85
+ @tagging_2.tagger = @tagger_2
86
+ @tagging_2.context = 'Science'
87
+ @tagging_2.tenant = 'account1'
88
+ @tagging_2.save
89
+
90
+ @tagging_3.taggable = TaggableModel.create(name: "Satellites")
91
+ @tagging_3.tag = ActsAsTaggableOn::Tag.create(name: "Engineering")
92
+ @tagging_3.tagger = @tagger_2
93
+ @tagging_3.context = 'Astronomy'
94
+ @tagging_3.save
95
+
96
+ end
97
+
98
+ describe '.owned_by' do
99
+ it "should belong to a specific user" do
100
+ expect(ActsAsTaggableOn::Tagging.owned_by(@tagger).first).to eq(@tagging)
101
+ end
102
+ end
103
+
104
+ describe '.by_context' do
105
+ it "should be found by context" do
106
+ expect(ActsAsTaggableOn::Tagging.by_context('Science').length).to eq(2);
107
+ end
108
+ end
109
+
110
+ describe '.by_contexts' do
111
+ it "should find taggings by contexts" do
112
+ expect(ActsAsTaggableOn::Tagging.by_contexts(['Science', 'Astronomy']).first).to eq(@tagging);
113
+ expect(ActsAsTaggableOn::Tagging.by_contexts(['Science', 'Astronomy']).second).to eq(@tagging_2);
114
+ expect(ActsAsTaggableOn::Tagging.by_contexts(['Science', 'Astronomy']).third).to eq(@tagging_3);
115
+ expect(ActsAsTaggableOn::Tagging.by_contexts(['Science', 'Astronomy']).length).to eq(3);
116
+ end
117
+ end
118
+
119
+ describe '.by_tenant' do
120
+ it "should find taggings by tenant" do
121
+ expect(ActsAsTaggableOn::Tagging.by_tenant('account1').length).to eq(2);
122
+ expect(ActsAsTaggableOn::Tagging.by_tenant('account1').first).to eq(@tagging);
123
+ expect(ActsAsTaggableOn::Tagging.by_tenant('account1').second).to eq(@tagging_2);
124
+ end
125
+ end
126
+
127
+ describe '.not_owned' do
128
+ before do
129
+ @tagging_4 = ActsAsTaggableOn::Tagging.new
130
+ @tagging_4.taggable = TaggableModel.create(name: "Gravity")
131
+ @tagging_4.tag = ActsAsTaggableOn::Tag.create(name: "Space")
132
+ @tagging_4.context = "Science"
133
+ @tagging_4.save
134
+ end
135
+
136
+ it "should found the taggings that do not have owner" do
137
+ expect(ActsAsTaggableOn::Tagging.all.length).to eq(4)
138
+ expect(ActsAsTaggableOn::Tagging.not_owned.length).to eq(1)
139
+ expect(ActsAsTaggableOn::Tagging.not_owned.first).to eq(@tagging_4)
140
+ end
141
+ end
142
+ end
63
143
  end
@@ -1,3 +1,5 @@
1
+ require_relative 'taggable_model'
2
+
1
3
  class AlteredInheritingTaggableModel < TaggableModel
2
4
  acts_as_taggable_on :parts
3
5
  end