acts-as-taggable-on 7.0.0 → 9.0.1

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +76 -0
  3. data/Appraisals +13 -13
  4. data/CHANGELOG.md +27 -2
  5. data/Gemfile +1 -0
  6. data/README.md +32 -7
  7. data/acts-as-taggable-on.gemspec +2 -2
  8. data/db/migrate/1_acts_as_taggable_on_migration.rb +5 -8
  9. data/db/migrate/2_add_missing_unique_indices.rb +6 -8
  10. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +3 -6
  11. data/db/migrate/4_add_missing_taggable_index.rb +5 -7
  12. data/db/migrate/5_change_collation_for_tag_names.rb +4 -6
  13. data/db/migrate/6_add_missing_indexes_on_taggings.rb +15 -13
  14. data/db/migrate/7_add_tenant_to_taggings.rb +13 -0
  15. data/docker-compose.yml +15 -0
  16. data/gemfiles/activerecord_6.0.gemfile +5 -8
  17. data/gemfiles/activerecord_6.1.gemfile +3 -8
  18. data/gemfiles/{activerecord_5.2.gemfile → activerecord_7.0.gemfile} +6 -9
  19. data/lib/acts_as_taggable_on/default_parser.rb +8 -10
  20. data/lib/acts_as_taggable_on/engine.rb +2 -0
  21. data/lib/acts_as_taggable_on/generic_parser.rb +2 -0
  22. data/lib/acts_as_taggable_on/tag.rb +33 -27
  23. data/lib/acts_as_taggable_on/tag_list.rb +8 -11
  24. data/lib/acts_as_taggable_on/taggable/cache.rb +64 -62
  25. data/lib/acts_as_taggable_on/taggable/collection.rb +178 -142
  26. data/lib/acts_as_taggable_on/taggable/core.rb +250 -236
  27. data/lib/acts_as_taggable_on/taggable/ownership.rb +110 -98
  28. data/lib/acts_as_taggable_on/taggable/related.rb +60 -47
  29. data/lib/acts_as_taggable_on/taggable/tag_list_type.rb +6 -2
  30. data/lib/acts_as_taggable_on/taggable/tagged_with_query/all_tags_query.rb +110 -106
  31. data/lib/acts_as_taggable_on/taggable/tagged_with_query/any_tags_query.rb +57 -53
  32. data/lib/acts_as_taggable_on/taggable/tagged_with_query/exclude_tags_query.rb +63 -60
  33. data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +54 -46
  34. data/lib/acts_as_taggable_on/taggable/tagged_with_query.rb +14 -8
  35. data/lib/acts_as_taggable_on/taggable.rb +30 -12
  36. data/lib/acts_as_taggable_on/tagger.rb +9 -5
  37. data/lib/acts_as_taggable_on/tagging.rb +8 -4
  38. data/lib/acts_as_taggable_on/tags_helper.rb +3 -1
  39. data/lib/acts_as_taggable_on/utils.rb +4 -2
  40. data/lib/acts_as_taggable_on/version.rb +3 -1
  41. data/spec/acts_as_taggable_on/tag_spec.rb +16 -1
  42. data/spec/acts_as_taggable_on/taggable_spec.rb +6 -2
  43. data/spec/acts_as_taggable_on/tagging_spec.rb +26 -0
  44. data/spec/internal/app/models/taggable_model.rb +2 -0
  45. data/spec/internal/config/database.yml.sample +4 -8
  46. data/spec/internal/db/schema.rb +3 -0
  47. data/spec/support/database.rb +36 -26
  48. metadata +13 -22
  49. data/.travis.yml +0 -49
  50. data/UPGRADING.md +0 -8
  51. data/gemfiles/activerecord_5.0.gemfile +0 -21
  52. data/gemfiles/activerecord_5.1.gemfile +0 -21
@@ -1,48 +1,51 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'tagged_with_query'
2
4
  require_relative 'tag_list_type'
3
5
 
4
- module ActsAsTaggableOn::Taggable
5
- module Core
6
+ module ActsAsTaggableOn
7
+ module Taggable
8
+ module Core
9
+ def self.included(base)
10
+ base.extend ActsAsTaggableOn::Taggable::Core::ClassMethods
6
11
 
7
- def self.included(base)
8
- base.extend ActsAsTaggableOn::Taggable::Core::ClassMethods
12
+ base.class_eval do
13
+ attr_writer :custom_contexts
9
14
 
10
- base.class_eval do
11
- attr_writer :custom_contexts
12
- after_save :save_tags
13
- end
15
+ after_save :save_tags
16
+ end
14
17
 
15
- base.initialize_acts_as_taggable_on_core
16
- end
18
+ base.initialize_acts_as_taggable_on_core
19
+ end
17
20
 
18
- module ClassMethods
19
- def initialize_acts_as_taggable_on_core
20
- include taggable_mixin
21
- tag_types.map(&:to_s).each do |tags_type|
22
- tag_type = tags_type.to_s.singularize
23
- context_taggings = "#{tag_type}_taggings".to_sym
24
- context_tags = tags_type.to_sym
25
- taggings_order = (preserve_tag_order? ? "#{ActsAsTaggableOn::Tagging.table_name}.id" : [])
26
-
27
- class_eval do
28
- # when preserving tag order, include order option so that for a 'tags' context
29
- # the associations tag_taggings & tags are always returned in created order
30
- has_many context_taggings, -> { includes(:tag).order(taggings_order).where(context: tags_type) },
31
- as: :taggable,
32
- class_name: 'ActsAsTaggableOn::Tagging',
33
- dependent: :destroy,
34
- after_add: :dirtify_tag_list,
35
- after_remove: :dirtify_tag_list
36
-
37
- has_many context_tags, -> { order(taggings_order) },
38
- class_name: 'ActsAsTaggableOn::Tag',
39
- through: context_taggings,
40
- source: :tag
41
-
42
- attribute "#{tags_type.singularize}_list".to_sym, ActsAsTaggableOn::Taggable::TagListType.new
43
- end
21
+ module ClassMethods
22
+ def initialize_acts_as_taggable_on_core
23
+ include taggable_mixin
24
+ tag_types.map(&:to_s).each do |tags_type|
25
+ tag_type = tags_type.to_s.singularize
26
+ context_taggings = "#{tag_type}_taggings".to_sym
27
+ context_tags = tags_type.to_sym
28
+ taggings_order = (preserve_tag_order? ? "#{ActsAsTaggableOn::Tagging.table_name}.id" : [])
29
+
30
+ class_eval do
31
+ # when preserving tag order, include order option so that for a 'tags' context
32
+ # the associations tag_taggings & tags are always returned in created order
33
+ has_many context_taggings, -> { includes(:tag).order(taggings_order).where(context: tags_type) },
34
+ as: :taggable,
35
+ class_name: 'ActsAsTaggableOn::Tagging',
36
+ dependent: :destroy,
37
+ after_add: :dirtify_tag_list,
38
+ after_remove: :dirtify_tag_list
39
+
40
+ has_many context_tags, -> { order(taggings_order) },
41
+ class_name: 'ActsAsTaggableOn::Tag',
42
+ through: context_taggings,
43
+ source: :tag
44
+
45
+ attribute "#{tags_type.singularize}_list".to_sym, ActsAsTaggableOn::Taggable::TagListType.new
46
+ end
44
47
 
45
- taggable_mixin.class_eval <<-RUBY, __FILE__, __LINE__ + 1
48
+ taggable_mixin.class_eval <<-RUBY, __FILE__, __LINE__ + 1
46
49
  def #{tag_type}_list
47
50
  tag_list_on('#{tags_type}')
48
51
  end
@@ -51,12 +54,8 @@ module ActsAsTaggableOn::Taggable
51
54
  parsed_new_list = ActsAsTaggableOn.default_parser.new(new_tags).parse
52
55
 
53
56
  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
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)
60
59
  end
61
60
  write_attribute("#{tag_type}_list", parsed_new_list)
62
61
  end
@@ -72,253 +71,268 @@ module ActsAsTaggableOn::Taggable
72
71
  def dirtify_tag_list(tagging)
73
72
  attribute_will_change! tagging.context.singularize+"_list"
74
73
  end
75
- RUBY
74
+ RUBY
75
+ end
76
+ end
77
+
78
+ def taggable_on(preserve_tag_order, *tag_types)
79
+ super(preserve_tag_order, *tag_types)
80
+ initialize_acts_as_taggable_on_core
76
81
  end
77
- end
78
82
 
79
- def taggable_on(preserve_tag_order, *tag_types)
80
- super(preserve_tag_order, *tag_types)
81
- initialize_acts_as_taggable_on_core
83
+ # all column names are necessary for PostgreSQL group clause
84
+ def grouped_column_names_for(object)
85
+ object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(', ')
86
+ end
87
+
88
+ ##
89
+ # Return a scope of objects that are tagged with the specified tags.
90
+ #
91
+ # @param tags The tags that we want to query for
92
+ # @param [Hash] options A hash of options to alter you query:
93
+ # * <tt>:exclude</tt> - if set to true, return objects that are *NOT* tagged with the specified tags
94
+ # * <tt>:any</tt> - if set to true, return objects that are tagged with *ANY* of the specified tags
95
+ # * <tt>:order_by_matching_tag_count</tt> - if set to true and used with :any, sort by objects matching the most tags, descending
96
+ # * <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
97
+ # * <tt>:owned_by</tt> - return objects that are *ONLY* owned by the owner
98
+ # * <tt>:start_at</tt> - Restrict the tags to those created after a certain time
99
+ # * <tt>:end_at</tt> - Restrict the tags to those created before a certain time
100
+ #
101
+ # Example:
102
+ # User.tagged_with(["awesome", "cool"]) # Users that are tagged with awesome and cool
103
+ # User.tagged_with(["awesome", "cool"], :exclude => true) # Users that are not tagged with awesome or cool
104
+ # User.tagged_with(["awesome", "cool"], :any => true) # Users that are tagged with awesome or cool
105
+ # User.tagged_with(["awesome", "cool"], :any => true, :order_by_matching_tag_count => true) # Sort by users who match the most tags, descending
106
+ # User.tagged_with(["awesome", "cool"], :match_all => true) # Users that are tagged with just awesome and cool
107
+ # User.tagged_with(["awesome", "cool"], :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
108
+ # User.tagged_with(["awesome", "cool"], :owned_by => foo, :start_at => Date.today ) # Users that are tagged with just awesome, cool by 'foo' and starting today
109
+ def tagged_with(tags, options = {})
110
+ tag_list = ActsAsTaggableOn.default_parser.new(tags).parse
111
+ options = options.dup
112
+
113
+ return none if tag_list.empty?
114
+
115
+ ::ActsAsTaggableOn::Taggable::TaggedWithQuery.build(self, ActsAsTaggableOn::Tag, ActsAsTaggableOn::Tagging,
116
+ tag_list, options)
117
+ end
118
+
119
+ def is_taggable?
120
+ true
121
+ end
122
+
123
+ def taggable_mixin
124
+ @taggable_mixin ||= Module.new
125
+ end
82
126
  end
83
127
 
84
128
  # all column names are necessary for PostgreSQL group clause
85
129
  def grouped_column_names_for(object)
86
- object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(', ')
130
+ self.class.grouped_column_names_for(object)
87
131
  end
88
132
 
89
- ##
90
- # Return a scope of objects that are tagged with the specified tags.
91
- #
92
- # @param tags The tags that we want to query for
93
- # @param [Hash] options A hash of options to alter you query:
94
- # * <tt>:exclude</tt> - if set to true, return objects that are *NOT* tagged with the specified tags
95
- # * <tt>:any</tt> - if set to true, return objects that are tagged with *ANY* of the specified tags
96
- # * <tt>:order_by_matching_tag_count</tt> - if set to true and used with :any, sort by objects matching the most tags, descending
97
- # * <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
98
- # * <tt>:owned_by</tt> - return objects that are *ONLY* owned by the owner
99
- # * <tt>:start_at</tt> - Restrict the tags to those created after a certain time
100
- # * <tt>:end_at</tt> - Restrict the tags to those created before a certain time
101
- #
102
- # Example:
103
- # User.tagged_with(["awesome", "cool"]) # Users that are tagged with awesome and cool
104
- # User.tagged_with(["awesome", "cool"], :exclude => true) # Users that are not tagged with awesome or cool
105
- # User.tagged_with(["awesome", "cool"], :any => true) # Users that are tagged with awesome or cool
106
- # User.tagged_with(["awesome", "cool"], :any => true, :order_by_matching_tag_count => true) # Sort by users who match the most tags, descending
107
- # User.tagged_with(["awesome", "cool"], :match_all => true) # Users that are tagged with just awesome and cool
108
- # User.tagged_with(["awesome", "cool"], :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
109
- # User.tagged_with(["awesome", "cool"], :owned_by => foo, :start_at => Date.today ) # Users that are tagged with just awesome, cool by 'foo' and starting today
110
- def tagged_with(tags, options = {})
111
- tag_list = ActsAsTaggableOn.default_parser.new(tags).parse
112
- options = options.dup
113
-
114
- return none if tag_list.empty?
115
-
116
- ::ActsAsTaggableOn::Taggable::TaggedWithQuery.build(self, ActsAsTaggableOn::Tag, ActsAsTaggableOn::Tagging, tag_list, options)
133
+ def custom_contexts
134
+ @custom_contexts ||= taggings.map(&:context).uniq
117
135
  end
118
136
 
119
137
  def is_taggable?
120
- true
138
+ self.class.is_taggable?
121
139
  end
122
140
 
123
- def taggable_mixin
124
- @taggable_mixin ||= Module.new
141
+ def add_custom_context(value)
142
+ unless custom_contexts.include?(value.to_s) || self.class.tag_types.map(&:to_s).include?(value.to_s)
143
+ custom_contexts << value.to_s
144
+ end
125
145
  end
126
- end
127
-
128
- # all column names are necessary for PostgreSQL group clause
129
- def grouped_column_names_for(object)
130
- self.class.grouped_column_names_for(object)
131
- end
132
146
 
133
- def custom_contexts
134
- @custom_contexts ||= taggings.map(&:context).uniq
135
- end
147
+ def cached_tag_list_on(context)
148
+ self["cached_#{context.to_s.singularize}_list"]
149
+ end
136
150
 
137
- def is_taggable?
138
- self.class.is_taggable?
139
- end
151
+ def tag_list_cache_set_on(context)
152
+ variable_name = "@#{context.to_s.singularize}_list"
153
+ instance_variable_defined?(variable_name) && instance_variable_get(variable_name)
154
+ end
140
155
 
141
- def add_custom_context(value)
142
- custom_contexts << value.to_s unless custom_contexts.include?(value.to_s) or self.class.tag_types.map(&:to_s).include?(value.to_s)
143
- end
156
+ def tag_list_cache_on(context)
157
+ variable_name = "@#{context.to_s.singularize}_list"
158
+ if instance_variable_get(variable_name)
159
+ instance_variable_get(variable_name)
160
+ elsif cached_tag_list_on(context) && ensure_included_cache_methods! && self.class.caching_tag_list_on?(context)
161
+ instance_variable_set(variable_name, ActsAsTaggableOn.default_parser.new(cached_tag_list_on(context)).parse)
162
+ else
163
+ instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
164
+ end
165
+ end
144
166
 
145
- def cached_tag_list_on(context)
146
- self["cached_#{context.to_s.singularize}_list"]
147
- end
167
+ def tag_list_on(context)
168
+ add_custom_context(context)
169
+ tag_list_cache_on(context)
170
+ end
148
171
 
149
- def tag_list_cache_set_on(context)
150
- variable_name = "@#{context.to_s.singularize}_list"
151
- instance_variable_defined?(variable_name) && instance_variable_get(variable_name)
152
- end
172
+ def all_tags_list_on(context)
173
+ variable_name = "@all_#{context.to_s.singularize}_list"
174
+ if instance_variable_defined?(variable_name) && instance_variable_get(variable_name)
175
+ return instance_variable_get(variable_name)
176
+ end
153
177
 
154
- def tag_list_cache_on(context)
155
- variable_name = "@#{context.to_s.singularize}_list"
156
- if instance_variable_get(variable_name)
157
- instance_variable_get(variable_name)
158
- elsif cached_tag_list_on(context) && ensure_included_cache_methods! && self.class.caching_tag_list_on?(context)
159
- instance_variable_set(variable_name, ActsAsTaggableOn.default_parser.new(cached_tag_list_on(context)).parse)
160
- else
161
- instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
178
+ instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(all_tags_on(context).map(&:name)).freeze)
162
179
  end
163
- end
164
-
165
- def tag_list_on(context)
166
- add_custom_context(context)
167
- tag_list_cache_on(context)
168
- end
169
180
 
170
- def all_tags_list_on(context)
171
- variable_name = "@all_#{context.to_s.singularize}_list"
172
- return instance_variable_get(variable_name) if instance_variable_defined?(variable_name) && instance_variable_get(variable_name)
181
+ ##
182
+ # Returns all tags of a given context
183
+ def all_tags_on(context)
184
+ tagging_table_name = ActsAsTaggableOn::Tagging.table_name
173
185
 
174
- instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(all_tags_on(context).map(&:name)).freeze)
175
- end
186
+ opts = ["#{tagging_table_name}.context = ?", context.to_s]
187
+ scope = base_tags.where(opts)
176
188
 
177
- ##
178
- # Returns all tags of a given context
179
- def all_tags_on(context)
180
- tagging_table_name = ActsAsTaggableOn::Tagging.table_name
189
+ if ActsAsTaggableOn::Utils.using_postgresql?
190
+ group_columns = grouped_column_names_for(ActsAsTaggableOn::Tag)
191
+ scope.order(Arel.sql("max(#{tagging_table_name}.created_at)")).group(group_columns)
192
+ else
193
+ scope.group("#{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key}")
194
+ end.to_a
195
+ end
181
196
 
182
- opts = ["#{tagging_table_name}.context = ?", context.to_s]
183
- scope = base_tags.where(opts)
197
+ ##
198
+ # Returns all tags that are not owned of a given context
199
+ def tags_on(context)
200
+ scope = base_tags.where([
201
+ "#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id IS NULL", context.to_s
202
+ ])
203
+ # when preserving tag order, return tags in created order
204
+ # if we added the order to the association this would always apply
205
+ scope = scope.order("#{ActsAsTaggableOn::Tagging.table_name}.id") if self.class.preserve_tag_order?
206
+ scope
207
+ end
184
208
 
185
- if ActsAsTaggableOn::Utils.using_postgresql?
186
- group_columns = grouped_column_names_for(ActsAsTaggableOn::Tag)
187
- scope.order(Arel.sql("max(#{tagging_table_name}.created_at)")).group(group_columns)
188
- else
189
- scope.group("#{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key}")
190
- end.to_a
191
- end
209
+ def set_tag_list_on(context, new_list)
210
+ add_custom_context(context)
192
211
 
193
- ##
194
- # Returns all tags that are not owned of a given context
195
- def tags_on(context)
196
- scope = base_tags.where(["#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id IS NULL", context.to_s])
197
- # when preserving tag order, return tags in created order
198
- # if we added the order to the association this would always apply
199
- scope = scope.order("#{ActsAsTaggableOn::Tagging.table_name}.id") if self.class.preserve_tag_order?
200
- scope
201
- end
212
+ variable_name = "@#{context.to_s.singularize}_list"
202
213
 
203
- def set_tag_list_on(context, new_list)
204
- add_custom_context(context)
214
+ parsed_new_list = ActsAsTaggableOn.default_parser.new(new_list).parse
205
215
 
206
- variable_name = "@#{context.to_s.singularize}_list"
216
+ instance_variable_set(variable_name, parsed_new_list)
217
+ end
207
218
 
208
- parsed_new_list = ActsAsTaggableOn.default_parser.new(new_list).parse
219
+ def tagging_contexts
220
+ self.class.tag_types.map(&:to_s) + custom_contexts
221
+ end
209
222
 
210
- instance_variable_set(variable_name, parsed_new_list)
211
- end
223
+ def taggable_tenant
224
+ public_send(self.class.tenant_column) if self.class.tenant_column
225
+ end
212
226
 
213
- def tagging_contexts
214
- self.class.tag_types.map(&:to_s) + custom_contexts
215
- end
227
+ def reload(*args)
228
+ self.class.tag_types.each do |context|
229
+ instance_variable_set("@#{context.to_s.singularize}_list", nil)
230
+ instance_variable_set("@all_#{context.to_s.singularize}_list", nil)
231
+ end
216
232
 
217
- def reload(*args)
218
- self.class.tag_types.each do |context|
219
- instance_variable_set("@#{context.to_s.singularize}_list", nil)
220
- instance_variable_set("@all_#{context.to_s.singularize}_list", nil)
233
+ super(*args)
221
234
  end
222
235
 
223
- super(*args)
224
- end
236
+ ##
237
+ # Find existing tags or create non-existing tags
238
+ def load_tags(tag_list)
239
+ ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_list)
240
+ end
225
241
 
226
- ##
227
- # Find existing tags or create non-existing tags
228
- def load_tags(tag_list)
229
- ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_list)
230
- end
242
+ def save_tags
243
+ tagging_contexts.each do |context|
244
+ next unless tag_list_cache_set_on(context)
231
245
 
232
- def save_tags
233
- tagging_contexts.each do |context|
234
- next unless tag_list_cache_set_on(context)
235
- # List of currently assigned tag names
236
- tag_list = tag_list_cache_on(context).uniq
246
+ # List of currently assigned tag names
247
+ tag_list = tag_list_cache_on(context).uniq
237
248
 
238
- # Find existing tags or create non-existing tags:
239
- tags = find_or_create_tags_from_list_with_context(tag_list, context)
249
+ # Find existing tags or create non-existing tags:
250
+ tags = find_or_create_tags_from_list_with_context(tag_list, context)
240
251
 
241
- # Tag objects for currently assigned tags
242
- current_tags = tags_on(context)
252
+ # Tag objects for currently assigned tags
253
+ current_tags = tags_on(context)
243
254
 
244
- # Tag maintenance based on whether preserving the created order of tags
245
- if self.class.preserve_tag_order?
246
- old_tags, new_tags = current_tags - tags, tags - current_tags
255
+ # Tag maintenance based on whether preserving the created order of tags
256
+ old_tags = current_tags - tags
257
+ new_tags = tags - current_tags
258
+ if self.class.preserve_tag_order?
247
259
 
248
- shared_tags = current_tags & tags
260
+ shared_tags = current_tags & tags
249
261
 
250
- if shared_tags.any? && tags[0...shared_tags.size] != shared_tags
251
- index = shared_tags.each_with_index { |_, i| break i unless shared_tags[i] == tags[i] }
262
+ if shared_tags.any? && tags[0...shared_tags.size] != shared_tags
263
+ index = shared_tags.each_with_index do |_, i|
264
+ break i unless shared_tags[i] == tags[i]
265
+ end
252
266
 
253
- # Update arrays of tag objects
254
- old_tags |= current_tags[index...current_tags.size]
255
- new_tags |= current_tags[index...current_tags.size] & shared_tags
267
+ # Update arrays of tag objects
268
+ old_tags |= current_tags[index...current_tags.size]
269
+ new_tags |= current_tags[index...current_tags.size] & shared_tags
256
270
 
257
- # Order the array of tag objects to match the tag list
258
- new_tags = tags.map do |t|
259
- new_tags.find { |n| n.name.downcase == t.name.downcase }
260
- end.compact
271
+ # Order the array of tag objects to match the tag list
272
+ new_tags = tags.map do |t|
273
+ new_tags.find { |n| n.name.downcase == t.name.downcase }
274
+ end.compact
275
+ end
276
+ else
277
+ # Delete discarded tags and create new tags
261
278
  end
262
- else
263
- # Delete discarded tags and create new tags
264
- old_tags = current_tags - tags
265
- new_tags = tags - current_tags
266
- end
267
279
 
268
- # Destroy old taggings:
269
- if old_tags.present?
270
- taggings.not_owned.by_context(context).where(tag_id: old_tags).destroy_all
271
- end
280
+ # Destroy old taggings:
281
+ taggings.not_owned.by_context(context).where(tag_id: old_tags).destroy_all if old_tags.present?
272
282
 
273
- # Create new taggings:
274
- new_tags.each do |tag|
275
- taggings.create!(tag_id: tag.id, context: context.to_s, taggable: self)
283
+ # Create new taggings:
284
+ new_tags.each do |tag|
285
+ if taggable_tenant
286
+ taggings.create!(tag_id: tag.id, context: context.to_s, taggable: self, tenant: taggable_tenant)
287
+ else
288
+ taggings.create!(tag_id: tag.id, context: context.to_s, taggable: self)
289
+ end
290
+ end
276
291
  end
277
- end
278
292
 
279
- true
280
- end
293
+ true
294
+ end
281
295
 
282
- private
296
+ private
283
297
 
284
- def ensure_included_cache_methods!
285
- self.class.columns
286
- end
298
+ def ensure_included_cache_methods!
299
+ self.class.columns
300
+ end
287
301
 
288
- # Filters the tag lists from the attribute names.
289
- def attributes_for_update(attribute_names)
290
- tag_lists = tag_types.map {|tags_type| "#{tags_type.to_s.singularize}_list"}
291
- super.delete_if {|attr| tag_lists.include? attr }
292
- end
302
+ # Filters the tag lists from the attribute names.
303
+ def attributes_for_update(attribute_names)
304
+ tag_lists = tag_types.map { |tags_type| "#{tags_type.to_s.singularize}_list" }
305
+ super.delete_if { |attr| tag_lists.include? attr }
306
+ end
293
307
 
294
- # Filters the tag lists from the attribute names.
295
- def attributes_for_create(attribute_names)
296
- tag_lists = tag_types.map {|tags_type| "#{tags_type.to_s.singularize}_list"}
297
- super.delete_if {|attr| tag_lists.include? attr }
298
- end
308
+ # Filters the tag lists from the attribute names.
309
+ def attributes_for_create(attribute_names)
310
+ tag_lists = tag_types.map { |tags_type| "#{tags_type.to_s.singularize}_list" }
311
+ super.delete_if { |attr| tag_lists.include? attr }
312
+ end
299
313
 
300
- ##
301
- # Override this hook if you wish to subclass {ActsAsTaggableOn::Tag} --
302
- # context is provided so that you may conditionally use a Tag subclass
303
- # only for some contexts.
304
- #
305
- # @example Custom Tag class for one context
306
- # class Company < ActiveRecord::Base
307
- # acts_as_taggable_on :markets, :locations
308
- #
309
- # def find_or_create_tags_from_list_with_context(tag_list, context)
310
- # if context.to_sym == :markets
311
- # MarketTag.find_or_create_all_with_like_by_name(tag_list)
312
- # else
313
- # super
314
- # end
315
- # end
316
- #
317
- # @param [Array<String>] tag_list Tags to find or create
318
- # @param [Symbol] context The tag context for the tag_list
319
- def find_or_create_tags_from_list_with_context(tag_list, _context)
320
- load_tags(tag_list)
314
+ ##
315
+ # Override this hook if you wish to subclass {ActsAsTaggableOn::Tag} --
316
+ # context is provided so that you may conditionally use a Tag subclass
317
+ # only for some contexts.
318
+ #
319
+ # @example Custom Tag class for one context
320
+ # class Company < ActiveRecord::Base
321
+ # acts_as_taggable_on :markets, :locations
322
+ #
323
+ # def find_or_create_tags_from_list_with_context(tag_list, context)
324
+ # if context.to_sym == :markets
325
+ # MarketTag.find_or_create_all_with_like_by_name(tag_list)
326
+ # else
327
+ # super
328
+ # end
329
+ # end
330
+ #
331
+ # @param [Array<String>] tag_list Tags to find or create
332
+ # @param [Symbol] context The tag context for the tag_list
333
+ def find_or_create_tags_from_list_with_context(tag_list, _context)
334
+ load_tags(tag_list)
335
+ end
321
336
  end
322
337
  end
323
338
  end
324
-