acts-as-taggable-on 6.0.0 → 8.1.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/.github/workflows/spec.yml +95 -0
- data/Appraisals +8 -0
- data/CHANGELOG.md +199 -140
- data/Gemfile +1 -0
- data/README.md +38 -4
- data/acts-as-taggable-on.gemspec +2 -2
- data/db/migrate/1_acts_as_taggable_on_migration.rb +8 -7
- data/db/migrate/2_add_missing_unique_indices.rb +8 -8
- data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +3 -3
- data/db/migrate/4_add_missing_taggable_index.rb +2 -2
- data/db/migrate/5_change_collation_for_tag_names.rb +1 -1
- data/db/migrate/6_add_missing_indexes_on_taggings.rb +9 -9
- data/db/migrate/7_add_tenant_to_taggings.rb +16 -0
- data/gemfiles/activerecord_5.0.gemfile +2 -4
- data/gemfiles/activerecord_5.1.gemfile +2 -4
- data/gemfiles/activerecord_5.2.gemfile +2 -4
- data/gemfiles/activerecord_6.0.gemfile +21 -0
- data/gemfiles/activerecord_6.1.gemfile +23 -0
- data/lib/acts-as-taggable-on.rb +6 -2
- data/lib/acts_as_taggable_on/tag.rb +16 -19
- data/lib/acts_as_taggable_on/taggable/cache.rb +38 -34
- data/lib/acts_as_taggable_on/taggable/collection.rb +8 -6
- data/lib/acts_as_taggable_on/taggable/core.rb +22 -6
- data/lib/acts_as_taggable_on/taggable/tag_list_type.rb +4 -0
- data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +4 -4
- data/lib/acts_as_taggable_on/taggable.rb +18 -0
- data/lib/acts_as_taggable_on/tagger.rb +1 -1
- data/lib/acts_as_taggable_on/tagging.rb +6 -2
- data/lib/acts_as_taggable_on/utils.rb +4 -0
- data/lib/acts_as_taggable_on/version.rb +1 -2
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +4 -12
- data/spec/acts_as_taggable_on/caching_spec.rb +16 -10
- data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +12 -7
- data/spec/acts_as_taggable_on/tag_spec.rb +16 -1
- data/spec/acts_as_taggable_on/taggable_spec.rb +16 -12
- data/spec/acts_as_taggable_on/tagger_spec.rb +2 -2
- data/spec/acts_as_taggable_on/tagging_spec.rb +26 -0
- data/spec/internal/app/models/altered_inheriting_taggable_model.rb +2 -0
- data/spec/internal/app/models/cached_model_with_array.rb +6 -0
- data/spec/internal/app/models/columns_override_model.rb +5 -0
- data/spec/internal/app/models/company.rb +1 -1
- data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
- data/spec/internal/app/models/market.rb +1 -1
- data/spec/internal/app/models/non_standard_id_taggable_model.rb +1 -1
- data/spec/internal/app/models/student.rb +2 -0
- data/spec/internal/app/models/taggable_model.rb +3 -0
- data/spec/internal/app/models/user.rb +1 -1
- data/spec/internal/config/database.yml.sample +4 -8
- data/spec/internal/db/schema.rb +14 -5
- data/spec/spec_helper.rb +0 -1
- data/spec/support/database.rb +4 -4
- metadata +20 -20
- data/.travis.yml +0 -33
- data/UPGRADING.md +0 -8
- data/spec/internal/app/models/models.rb +0 -90
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'tagged_with_query'
|
2
|
+
require_relative 'tag_list_type'
|
2
3
|
|
3
4
|
module ActsAsTaggableOn::Taggable
|
4
5
|
module Core
|
@@ -38,7 +39,7 @@ module ActsAsTaggableOn::Taggable
|
|
38
39
|
through: context_taggings,
|
39
40
|
source: :tag
|
40
41
|
|
41
|
-
attribute "#{tags_type.singularize}_list".to_sym,
|
42
|
+
attribute "#{tags_type.singularize}_list".to_sym, ActsAsTaggableOn::Taggable::TagListType.new
|
42
43
|
end
|
43
44
|
|
44
45
|
taggable_mixin.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
@@ -49,8 +50,14 @@ module ActsAsTaggableOn::Taggable
|
|
49
50
|
def #{tag_type}_list=(new_tags)
|
50
51
|
parsed_new_list = ActsAsTaggableOn.default_parser.new(new_tags).parse
|
51
52
|
|
52
|
-
if self.class.preserve_tag_order? || parsed_new_list.sort != #{tag_type}_list.sort
|
53
|
-
|
53
|
+
if self.class.preserve_tag_order? || (parsed_new_list.sort != #{tag_type}_list.sort)
|
54
|
+
if ActsAsTaggableOn::Utils.legacy_activerecord?
|
55
|
+
set_attribute_was("#{tag_type}_list", #{tag_type}_list)
|
56
|
+
else
|
57
|
+
unless #{tag_type}_list_changed?
|
58
|
+
@attributes["#{tag_type}_list"] = ActiveModel::Attribute.from_user("#{tag_type}_list", #{tag_type}_list, ActsAsTaggableOn::Taggable::TagListType.new)
|
59
|
+
end
|
60
|
+
end
|
54
61
|
write_attribute("#{tag_type}_list", parsed_new_list)
|
55
62
|
end
|
56
63
|
|
@@ -103,9 +110,8 @@ module ActsAsTaggableOn::Taggable
|
|
103
110
|
def tagged_with(tags, options = {})
|
104
111
|
tag_list = ActsAsTaggableOn.default_parser.new(tags).parse
|
105
112
|
options = options.dup
|
106
|
-
empty_result = where('1 = 0')
|
107
113
|
|
108
|
-
return
|
114
|
+
return none if tag_list.empty?
|
109
115
|
|
110
116
|
::ActsAsTaggableOn::Taggable::TaggedWithQuery.build(self, ActsAsTaggableOn::Tag, ActsAsTaggableOn::Tagging, tag_list, options)
|
111
117
|
end
|
@@ -208,6 +214,12 @@ module ActsAsTaggableOn::Taggable
|
|
208
214
|
self.class.tag_types.map(&:to_s) + custom_contexts
|
209
215
|
end
|
210
216
|
|
217
|
+
def taggable_tenant
|
218
|
+
if self.class.tenant_column
|
219
|
+
public_send(self.class.tenant_column)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
211
223
|
def reload(*args)
|
212
224
|
self.class.tag_types.each do |context|
|
213
225
|
instance_variable_set("@#{context.to_s.singularize}_list", nil)
|
@@ -266,7 +278,11 @@ module ActsAsTaggableOn::Taggable
|
|
266
278
|
|
267
279
|
# Create new taggings:
|
268
280
|
new_tags.each do |tag|
|
269
|
-
|
281
|
+
if taggable_tenant
|
282
|
+
taggings.create!(tag_id: tag.id, context: context.to_s, taggable: self, tenant: taggable_tenant)
|
283
|
+
else
|
284
|
+
taggings.create!(tag_id: tag.id, context: context.to_s, taggable: self)
|
285
|
+
end
|
270
286
|
end
|
271
287
|
end
|
272
288
|
|
@@ -29,9 +29,9 @@ module ActsAsTaggableOn::Taggable::TaggedWithQuery
|
|
29
29
|
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
30
30
|
|
31
31
|
if options[:wild].present?
|
32
|
-
matches_attribute.matches("%#{escaped_tag(tag)}%", "!")
|
32
|
+
matches_attribute.matches("%#{escaped_tag(tag)}%", "!", ActsAsTaggableOn.strict_case_match)
|
33
33
|
else
|
34
|
-
matches_attribute.matches(escaped_tag(tag), "!")
|
34
|
+
matches_attribute.matches(escaped_tag(tag), "!", ActsAsTaggableOn.strict_case_match)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -40,9 +40,9 @@ module ActsAsTaggableOn::Taggable::TaggedWithQuery
|
|
40
40
|
matches_attribute = matches_attribute.lower unless ActsAsTaggableOn.strict_case_match
|
41
41
|
|
42
42
|
if options[:wild].present?
|
43
|
-
matches_attribute.matches_any(tag_list.map{|tag| "%#{escaped_tag(tag)}%"}, "!")
|
43
|
+
matches_attribute.matches_any(tag_list.map{|tag| "%#{escaped_tag(tag)}%"}, "!", ActsAsTaggableOn.strict_case_match)
|
44
44
|
else
|
45
|
-
matches_attribute.matches_any(tag_list.map{|tag| "#{escaped_tag(tag)}"}, "!")
|
45
|
+
matches_attribute.matches_any(tag_list.map{|tag| "#{escaped_tag(tag)}"}, "!", ActsAsTaggableOn.strict_case_match)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -54,6 +54,23 @@ module ActsAsTaggableOn
|
|
54
54
|
taggable_on(true, tag_types)
|
55
55
|
end
|
56
56
|
|
57
|
+
def acts_as_taggable_tenant(tenant)
|
58
|
+
if taggable?
|
59
|
+
self.tenant_column = tenant
|
60
|
+
else
|
61
|
+
class_attribute :tenant_column
|
62
|
+
self.tenant_column = tenant
|
63
|
+
end
|
64
|
+
|
65
|
+
# each of these add context-specific methods and must be
|
66
|
+
# called on each call of taggable_on
|
67
|
+
include Core
|
68
|
+
include Collection
|
69
|
+
include Cache
|
70
|
+
include Ownership
|
71
|
+
include Related
|
72
|
+
end
|
73
|
+
|
57
74
|
private
|
58
75
|
|
59
76
|
# Make a model taggable on specified contexts
|
@@ -78,6 +95,7 @@ module ActsAsTaggableOn
|
|
78
95
|
self.tag_types = tag_types
|
79
96
|
class_attribute :preserve_tag_order
|
80
97
|
self.preserve_tag_order = preserve_tag_order
|
98
|
+
class_attribute :tenant_column
|
81
99
|
|
82
100
|
class_eval do
|
83
101
|
has_many :taggings, as: :taggable, dependent: :destroy, class_name: '::ActsAsTaggableOn::Tagging'
|
@@ -1,10 +1,12 @@
|
|
1
1
|
module ActsAsTaggableOn
|
2
2
|
class Tagging < ::ActiveRecord::Base #:nodoc:
|
3
|
+
self.table_name = ActsAsTaggableOn.taggings_table
|
4
|
+
|
3
5
|
DEFAULT_CONTEXT = 'tags'
|
4
6
|
belongs_to :tag, class_name: '::ActsAsTaggableOn::Tag', counter_cache: ActsAsTaggableOn.tags_counter
|
5
7
|
belongs_to :taggable, polymorphic: true
|
6
8
|
|
7
|
-
belongs_to :tagger,
|
9
|
+
belongs_to :tagger, polymorphic: true, optional: true
|
8
10
|
|
9
11
|
scope :owned_by, ->(owner) { where(tagger: owner) }
|
10
12
|
scope :not_owned, -> { where(tagger_id: nil, tagger_type: nil) }
|
@@ -12,6 +14,8 @@ module ActsAsTaggableOn
|
|
12
14
|
scope :by_contexts, ->(contexts) { where(context: (contexts || DEFAULT_CONTEXT)) }
|
13
15
|
scope :by_context, ->(context = DEFAULT_CONTEXT) { by_contexts(context.to_s) }
|
14
16
|
|
17
|
+
scope :by_tenant, ->(tenant) { where(tenant: tenant) }
|
18
|
+
|
15
19
|
validates_presence_of :context
|
16
20
|
validates_presence_of :tag_id
|
17
21
|
|
@@ -26,7 +30,7 @@ module ActsAsTaggableOn
|
|
26
30
|
if ActsAsTaggableOn.tags_counter
|
27
31
|
tag.destroy if tag.reload.taggings_count.zero?
|
28
32
|
else
|
29
|
-
tag.destroy if tag.reload.taggings.
|
33
|
+
tag.destroy if tag.reload.taggings.none?
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -24,6 +24,10 @@ module ActsAsTaggableOn
|
|
24
24
|
using_postgresql? ? 'ILIKE' : 'LIKE'
|
25
25
|
end
|
26
26
|
|
27
|
+
def legacy_activerecord?
|
28
|
+
ActiveRecord.version <= Gem::Version.new('5.3.0')
|
29
|
+
end
|
30
|
+
|
27
31
|
# escape _ and % characters in strings, since these are wildcards in SQL.
|
28
32
|
def escape_like(str)
|
29
33
|
str.gsub(/[!%_]/) { |x| '!' + x }
|
@@ -73,14 +73,6 @@ describe 'Acts As Taggable On' do
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
describe 'Reloading' do
|
77
|
-
it 'should save a model instantiated by Model.find' do
|
78
|
-
taggable = TaggableModel.create!(name: 'Taggable')
|
79
|
-
found_taggable = TaggableModel.find(taggable.id)
|
80
|
-
found_taggable.save
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
76
|
describe 'Matching Contexts' do
|
85
77
|
it 'should find objects with tags of matching contexts' do
|
86
78
|
taggable1 = TaggableModel.create!(name: 'Taggable 1')
|
@@ -142,7 +134,7 @@ describe 'Acts As Taggable On' do
|
|
142
134
|
taggable1.save
|
143
135
|
|
144
136
|
column = TaggableModel.connection.quote_column_name("context")
|
145
|
-
offer_alias = TaggableModel.connection.quote_table_name(
|
137
|
+
offer_alias = TaggableModel.connection.quote_table_name(ActsAsTaggableOn.taggings_table)
|
146
138
|
need_alias = TaggableModel.connection.quote_table_name("need_taggings_taggable_models_join")
|
147
139
|
|
148
140
|
expect(TaggableModel.joins(:offerings, :needs).to_sql).to include "#{offer_alias}.#{column}"
|
@@ -200,7 +192,7 @@ describe 'Acts As Taggable On' do
|
|
200
192
|
its(:cached_glass_list) { should be_blank }
|
201
193
|
|
202
194
|
context 'language taggings cache after update' do
|
203
|
-
before { @taggable.
|
195
|
+
before { @taggable.update(language_list: 'ruby, .net') }
|
204
196
|
subject { @taggable }
|
205
197
|
|
206
198
|
its(:language_list) { should == ['ruby', '.net']}
|
@@ -209,7 +201,7 @@ describe 'Acts As Taggable On' do
|
|
209
201
|
end
|
210
202
|
|
211
203
|
context 'status taggings cache after update' do
|
212
|
-
before { @taggable.
|
204
|
+
before { @taggable.update(status_list: 'happy, married') }
|
213
205
|
subject { @taggable }
|
214
206
|
|
215
207
|
its(:status_list) { should == ['happy', 'married'] }
|
@@ -222,7 +214,7 @@ describe 'Acts As Taggable On' do
|
|
222
214
|
|
223
215
|
context 'glass taggings cache after update' do
|
224
216
|
before do
|
225
|
-
@taggable.
|
217
|
+
@taggable.update(glass_list: 'rectangle, aviator')
|
226
218
|
end
|
227
219
|
|
228
220
|
subject { @taggable }
|
@@ -31,40 +31,40 @@ describe 'Acts As Taggable On' do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'should cache tags' do
|
34
|
-
@taggable.
|
34
|
+
@taggable.update(tag_list: 'awesome, epic')
|
35
35
|
expect(@taggable.cached_tag_list).to eq('awesome, epic')
|
36
36
|
|
37
|
-
@another_taggable.
|
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.
|
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.
|
50
|
-
@taggable.
|
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.
|
56
|
-
@taggable.
|
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.
|
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.
|
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,12 @@ 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
|
78
84
|
end
|
79
85
|
|
80
86
|
describe 'with a custom delimiter' do
|
@@ -89,7 +95,7 @@ describe 'Acts As Taggable On' do
|
|
89
95
|
end
|
90
96
|
|
91
97
|
it 'should cache tags with custom delimiter' do
|
92
|
-
@taggable.
|
98
|
+
@taggable.update(tag_list: 'awesome; epic')
|
93
99
|
expect(@taggable.tag_list).to eq(['awesome', 'epic'])
|
94
100
|
expect(@taggable.cached_tag_list).to eq('awesome; epic')
|
95
101
|
|
@@ -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:
|
131
|
-
expect(AlteredInheritingTaggableModel.tag_counts_on(:tags, order:
|
132
|
-
expect(TaggableModel.tag_counts_on(:tags, order:
|
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:
|
142
|
-
expect(AlteredInheritingTaggableModel.tags_on(:tags, order:
|
143
|
-
expect(TaggableModel.tags_on(:tags, order:
|
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.
|
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
|
|
@@ -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
|
|
@@ -70,6 +70,21 @@ describe ActsAsTaggableOn::Tag do
|
|
70
70
|
end
|
71
71
|
end
|
72
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
|
+
|
73
88
|
describe 'find or create by name' do
|
74
89
|
before(:each) do
|
75
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
|
|
@@ -200,8 +204,8 @@ describe 'Taggable' do
|
|
200
204
|
@taggables[1].skill_list = 'css'
|
201
205
|
@taggables.each { |taggable| taggable.save }
|
202
206
|
|
203
|
-
@found_taggables_by_tag = TaggableModel.joins(:tags).where(
|
204
|
-
@found_taggables_by_skill = TaggableModel.joins(:skills).where(
|
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']})
|
205
209
|
|
206
210
|
expect(@found_taggables_by_tag).to include @taggables[0]
|
207
211
|
expect(@found_taggables_by_tag).to_not include @taggables[1]
|
@@ -338,7 +342,7 @@ describe 'Taggable' do
|
|
338
342
|
TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
|
339
343
|
|
340
344
|
expect(TaggableModel.all_tag_counts).to_not be_empty
|
341
|
-
expect(TaggableModel.all_tag_counts(order:
|
345
|
+
expect(TaggableModel.all_tag_counts(order: "#{ActsAsTaggableOn.tags_table}.id").first.count).to eq(3) # ruby
|
342
346
|
end
|
343
347
|
|
344
348
|
it 'should be able to get all tags on model as whole' do
|
@@ -347,7 +351,7 @@ describe 'Taggable' do
|
|
347
351
|
TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
|
348
352
|
|
349
353
|
expect(TaggableModel.all_tags).to_not be_empty
|
350
|
-
expect(TaggableModel.all_tags(order:
|
354
|
+
expect(TaggableModel.all_tags(order: "#{ActsAsTaggableOn.tags_table}.id").first.name).to eq('ruby')
|
351
355
|
end
|
352
356
|
|
353
357
|
it 'should be able to use named scopes to chain tag finds by any tags by context' do
|
@@ -369,7 +373,7 @@ describe 'Taggable' do
|
|
369
373
|
TaggableModel.create(name: 'Frank', tag_list: 'ruby, rails')
|
370
374
|
TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
|
371
375
|
|
372
|
-
expect(TaggableModel.tagged_with('ruby').tag_counts(order:
|
376
|
+
expect(TaggableModel.tagged_with('ruby').tag_counts(order: "#{ActsAsTaggableOn.tags_table}.id").first.count).to eq(2) # ruby
|
373
377
|
expect(TaggableModel.tagged_with('ruby').skill_counts.first.count).to eq(1) # ruby
|
374
378
|
end
|
375
379
|
|
@@ -378,7 +382,7 @@ describe 'Taggable' do
|
|
378
382
|
TaggableModel.create(name: 'Frank', tag_list: 'ruby, rails')
|
379
383
|
TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
|
380
384
|
|
381
|
-
expect(TaggableModel.tagged_with('ruby').all_tag_counts(order:
|
385
|
+
expect(TaggableModel.tagged_with('ruby').all_tag_counts(order: "#{ActsAsTaggableOn.tags_table}.id").first.count).to eq(3) # ruby
|
382
386
|
end
|
383
387
|
|
384
388
|
it 'should be able to get all scoped tags' do
|
@@ -386,7 +390,7 @@ describe 'Taggable' do
|
|
386
390
|
TaggableModel.create(name: 'Frank', tag_list: 'ruby, rails')
|
387
391
|
TaggableModel.create(name: 'Charlie', skill_list: 'ruby')
|
388
392
|
|
389
|
-
expect(TaggableModel.tagged_with('ruby').all_tags(order:
|
393
|
+
expect(TaggableModel.tagged_with('ruby').all_tags(order: "#{ActsAsTaggableOn.tags_table}.id").first.name).to eq('ruby')
|
390
394
|
end
|
391
395
|
|
392
396
|
it 'should only return tag counts for the available scope' do
|
@@ -477,7 +481,7 @@ describe 'Taggable' do
|
|
477
481
|
|
478
482
|
expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, any: true).to_a.sort_by { |o| o.id }).to eq([bob, frank, steve])
|
479
483
|
expect(TaggableModel.tagged_with(%w(bob tricia), wild: true, exclude: true).to_a).to eq([jim])
|
480
|
-
expect(TaggableModel.tagged_with('ji', wild: true, any: true).to_a
|
484
|
+
expect(TaggableModel.tagged_with('ji', wild: true, any: true).to_a).to match_array([frank, jim])
|
481
485
|
end
|
482
486
|
end
|
483
487
|
|
@@ -541,7 +545,7 @@ describe 'Taggable' do
|
|
541
545
|
|
542
546
|
it 'should not delete tags if not updated' do
|
543
547
|
model = TaggableModel.create(name: 'foo', tag_list: 'ruby, rails, programming')
|
544
|
-
model.
|
548
|
+
model.update(name: 'bar')
|
545
549
|
model.reload
|
546
550
|
expect(model.tag_list.sort).to eq(%w(ruby rails programming).sort)
|
547
551
|
end
|
@@ -672,15 +676,15 @@ describe 'Taggable' do
|
|
672
676
|
# NOTE: type column supports an STI Tag subclass in the test suite, though
|
673
677
|
# isn't included by default in the migration generator
|
674
678
|
expect(@taggable.grouped_column_names_for(ActsAsTaggableOn::Tag))
|
675
|
-
.to eq(
|
679
|
+
.to eq("#{ActsAsTaggableOn.tags_table}.id, #{ActsAsTaggableOn.tags_table}.name, #{ActsAsTaggableOn.tags_table}.taggings_count, #{ActsAsTaggableOn.tags_table}.type")
|
676
680
|
end
|
677
681
|
|
678
682
|
it 'should return all column names joined for TaggableModel GROUP clause' do
|
679
|
-
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')
|
680
684
|
end
|
681
685
|
|
682
686
|
it 'should return all column names joined for NonStandardIdTaggableModel GROUP clause' do
|
683
|
-
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")
|
684
688
|
end
|
685
689
|
end
|
686
690
|
|
@@ -112,7 +112,7 @@ describe 'Tagger' do
|
|
112
112
|
end
|
113
113
|
|
114
114
|
it 'should not lose tags' do
|
115
|
-
@taggable.
|
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.
|
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,6 +49,22 @@ describe ActsAsTaggableOn::Tagging do
|
|
49
49
|
ActsAsTaggableOn.remove_unused_tags = previous_setting
|
50
50
|
end
|
51
51
|
|
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
|
57
|
+
|
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)
|
63
|
+
|
64
|
+
ActsAsTaggableOn.remove_unused_tags = remove_unused_tags_previous_setting
|
65
|
+
ActsAsTaggableOn.tags_counter = tags_counter_previous_setting
|
66
|
+
end
|
67
|
+
|
52
68
|
describe 'context scopes' do
|
53
69
|
before do
|
54
70
|
@tagging_2 = ActsAsTaggableOn::Tagging.new
|
@@ -61,12 +77,14 @@ describe ActsAsTaggableOn::Tagging do
|
|
61
77
|
@tagging.tag = ActsAsTaggableOn::Tag.create(name: "Physics")
|
62
78
|
@tagging.tagger = @tagger
|
63
79
|
@tagging.context = 'Science'
|
80
|
+
@tagging.tenant = 'account1'
|
64
81
|
@tagging.save
|
65
82
|
|
66
83
|
@tagging_2.taggable = TaggableModel.create(name: "Satellites")
|
67
84
|
@tagging_2.tag = ActsAsTaggableOn::Tag.create(name: "Technology")
|
68
85
|
@tagging_2.tagger = @tagger_2
|
69
86
|
@tagging_2.context = 'Science'
|
87
|
+
@tagging_2.tenant = 'account1'
|
70
88
|
@tagging_2.save
|
71
89
|
|
72
90
|
@tagging_3.taggable = TaggableModel.create(name: "Satellites")
|
@@ -98,6 +116,14 @@ describe ActsAsTaggableOn::Tagging do
|
|
98
116
|
end
|
99
117
|
end
|
100
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
|
+
|
101
127
|
describe '.not_owned' do
|
102
128
|
before do
|
103
129
|
@tagging_4 = ActsAsTaggableOn::Tagging.new
|
@@ -1,2 +1,2 @@
|
|
1
1
|
class Market < ActsAsTaggableOn::Tag
|
2
|
-
end
|
2
|
+
end
|
@@ -3,9 +3,12 @@ class TaggableModel < ActiveRecord::Base
|
|
3
3
|
acts_as_taggable_on :languages
|
4
4
|
acts_as_taggable_on :skills
|
5
5
|
acts_as_taggable_on :needs, :offerings
|
6
|
+
acts_as_taggable_tenant :tenant_id
|
7
|
+
|
6
8
|
has_many :untaggable_models
|
7
9
|
|
8
10
|
attr_reader :tag_list_submethod_called
|
11
|
+
|
9
12
|
def tag_list=(v)
|
10
13
|
@tag_list_submethod_called = true
|
11
14
|
super
|