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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +95 -0
  3. data/Appraisals +8 -0
  4. data/CHANGELOG.md +199 -140
  5. data/Gemfile +1 -0
  6. data/README.md +38 -4
  7. data/acts-as-taggable-on.gemspec +2 -2
  8. data/db/migrate/1_acts_as_taggable_on_migration.rb +8 -7
  9. data/db/migrate/2_add_missing_unique_indices.rb +8 -8
  10. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +3 -3
  11. data/db/migrate/4_add_missing_taggable_index.rb +2 -2
  12. data/db/migrate/5_change_collation_for_tag_names.rb +1 -1
  13. data/db/migrate/6_add_missing_indexes_on_taggings.rb +9 -9
  14. data/db/migrate/7_add_tenant_to_taggings.rb +16 -0
  15. data/gemfiles/activerecord_5.0.gemfile +2 -4
  16. data/gemfiles/activerecord_5.1.gemfile +2 -4
  17. data/gemfiles/activerecord_5.2.gemfile +2 -4
  18. data/gemfiles/activerecord_6.0.gemfile +21 -0
  19. data/gemfiles/activerecord_6.1.gemfile +23 -0
  20. data/lib/acts-as-taggable-on.rb +6 -2
  21. data/lib/acts_as_taggable_on/tag.rb +16 -19
  22. data/lib/acts_as_taggable_on/taggable/cache.rb +38 -34
  23. data/lib/acts_as_taggable_on/taggable/collection.rb +8 -6
  24. data/lib/acts_as_taggable_on/taggable/core.rb +22 -6
  25. data/lib/acts_as_taggable_on/taggable/tag_list_type.rb +4 -0
  26. data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +4 -4
  27. data/lib/acts_as_taggable_on/taggable.rb +18 -0
  28. data/lib/acts_as_taggable_on/tagger.rb +1 -1
  29. data/lib/acts_as_taggable_on/tagging.rb +6 -2
  30. data/lib/acts_as_taggable_on/utils.rb +4 -0
  31. data/lib/acts_as_taggable_on/version.rb +1 -2
  32. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +4 -12
  33. data/spec/acts_as_taggable_on/caching_spec.rb +16 -10
  34. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +12 -7
  35. data/spec/acts_as_taggable_on/tag_spec.rb +16 -1
  36. data/spec/acts_as_taggable_on/taggable_spec.rb +16 -12
  37. data/spec/acts_as_taggable_on/tagger_spec.rb +2 -2
  38. data/spec/acts_as_taggable_on/tagging_spec.rb +26 -0
  39. data/spec/internal/app/models/altered_inheriting_taggable_model.rb +2 -0
  40. data/spec/internal/app/models/cached_model_with_array.rb +6 -0
  41. data/spec/internal/app/models/columns_override_model.rb +5 -0
  42. data/spec/internal/app/models/company.rb +1 -1
  43. data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
  44. data/spec/internal/app/models/market.rb +1 -1
  45. data/spec/internal/app/models/non_standard_id_taggable_model.rb +1 -1
  46. data/spec/internal/app/models/student.rb +2 -0
  47. data/spec/internal/app/models/taggable_model.rb +3 -0
  48. data/spec/internal/app/models/user.rb +1 -1
  49. data/spec/internal/config/database.yml.sample +4 -8
  50. data/spec/internal/db/schema.rb +14 -5
  51. data/spec/spec_helper.rb +0 -1
  52. data/spec/support/database.rb +4 -4
  53. metadata +20 -20
  54. data/.travis.yml +0 -33
  55. data/UPGRADING.md +0 -8
  56. 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, ActiveModel::Type::Value.new
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
- set_attribute_was('#{tag_type}_list', #{tag_type}_list)
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 empty_result if tag_list.empty?
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
- taggings.create!(tag_id: tag.id, context: context.to_s, taggable: self)
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
 
@@ -0,0 +1,4 @@
1
+ module ActsAsTaggableOn::Taggable
2
+ class TagListType < ActiveModel::Type::Value
3
+ end
4
+ end
@@ -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'
@@ -18,7 +18,7 @@ module ActsAsTaggableOn
18
18
  owned_taggings_scope = opts.delete(:scope)
19
19
 
20
20
  has_many :owned_taggings, owned_taggings_scope,
21
- opts.merge(
21
+ **opts.merge(
22
22
  as: :tagger,
23
23
  class_name: '::ActsAsTaggableOn::Tagging',
24
24
  dependent: :destroy
@@ -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, {polymorphic: true}.tap {|o| o.merge!(optional: true) }
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.count.zero?
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 }
@@ -1,4 +1,3 @@
1
1
  module ActsAsTaggableOn
2
- VERSION = '6.0.0'
2
+ VERSION = '8.1.0'
3
3
  end
4
-
@@ -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("taggings")
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.update_attributes(language_list: 'ruby, .net') }
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.update_attributes(status_list: 'happy, married') }
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.update_attributes(glass_list: 'rectangle, aviator')
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.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,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.update_attributes(tag_list: 'awesome; epic')
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: '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
 
@@ -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(tags: {name: ['bob']})
204
- @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']})
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: '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
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: 'tags.id').first.name).to eq('ruby')
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: '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
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: '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
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: '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')
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 =~ [frank, jim])
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.update_attributes(name: 'bar')
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('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")
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.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,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,3 +1,5 @@
1
+ require_relative 'taggable_model'
2
+
1
3
  class AlteredInheritingTaggableModel < TaggableModel
2
4
  acts_as_taggable_on :parts
3
5
  end
@@ -2,4 +2,10 @@ if using_postgresql?
2
2
  class CachedModelWithArray < ActiveRecord::Base
3
3
  acts_as_taggable
4
4
  end
5
+ if postgresql_support_json?
6
+ class TaggableModelWithJson < ActiveRecord::Base
7
+ acts_as_taggable
8
+ acts_as_taggable_on :skills
9
+ end
10
+ end
5
11
  end
@@ -0,0 +1,5 @@
1
+ class ColumnsOverrideModel < ActiveRecord::Base
2
+ def self.columns
3
+ super.reject { |c| c.name == 'ignored_column' }
4
+ end
5
+ end
@@ -12,4 +12,4 @@ class Company < ActiveRecord::Base
12
12
  super
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -1,2 +1,4 @@
1
+ require_relative 'taggable_model'
2
+
1
3
  class InheritingTaggableModel < TaggableModel
2
4
  end
@@ -1,2 +1,2 @@
1
1
  class Market < ActsAsTaggableOn::Tag
2
- end
2
+ end
@@ -1,5 +1,5 @@
1
1
  class NonStandardIdTaggableModel < ActiveRecord::Base
2
- self.primary_key = 'an_id'
2
+ self.primary_key = :an_id
3
3
  acts_as_taggable
4
4
  acts_as_taggable_on :languages
5
5
  acts_as_taggable_on :skills
@@ -1,2 +1,4 @@
1
+ require_relative 'user'
2
+
1
3
  class Student < User
2
4
  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
@@ -1,3 +1,3 @@
1
1
  class User < ActiveRecord::Base
2
2
  acts_as_tagger
3
- end
3
+ end