acts-as-taggable-on 8.1.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +15 -34
  3. data/Appraisals +13 -13
  4. data/CHANGELOG.md +17 -3
  5. data/README.md +6 -6
  6. data/acts-as-taggable-on.gemspec +2 -2
  7. data/db/migrate/1_acts_as_taggable_on_migration.rb +5 -8
  8. data/db/migrate/2_add_missing_unique_indices.rb +6 -8
  9. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +3 -6
  10. data/db/migrate/4_add_missing_taggable_index.rb +5 -7
  11. data/db/migrate/5_change_collation_for_tag_names.rb +4 -6
  12. data/db/migrate/6_add_missing_indexes_on_taggings.rb +15 -13
  13. data/db/migrate/7_add_tenant_to_taggings.rb +7 -10
  14. data/docker-compose.yml +15 -0
  15. data/gemfiles/activerecord_6.0.gemfile +5 -8
  16. data/gemfiles/activerecord_6.1.gemfile +3 -8
  17. data/gemfiles/{activerecord_5.0.gemfile → activerecord_7.0.gemfile} +6 -9
  18. data/lib/acts_as_taggable_on/default_parser.rb +8 -10
  19. data/lib/acts_as_taggable_on/engine.rb +2 -0
  20. data/lib/acts_as_taggable_on/generic_parser.rb +2 -0
  21. data/lib/acts_as_taggable_on/tag.rb +30 -30
  22. data/lib/acts_as_taggable_on/tag_list.rb +8 -11
  23. data/lib/acts_as_taggable_on/taggable/cache.rb +64 -62
  24. data/lib/acts_as_taggable_on/taggable/collection.rb +178 -142
  25. data/lib/acts_as_taggable_on/taggable/core.rb +248 -244
  26. data/lib/acts_as_taggable_on/taggable/ownership.rb +110 -98
  27. data/lib/acts_as_taggable_on/taggable/related.rb +60 -47
  28. data/lib/acts_as_taggable_on/taggable/tag_list_type.rb +6 -2
  29. data/lib/acts_as_taggable_on/taggable/tagged_with_query/all_tags_query.rb +110 -106
  30. data/lib/acts_as_taggable_on/taggable/tagged_with_query/any_tags_query.rb +57 -53
  31. data/lib/acts_as_taggable_on/taggable/tagged_with_query/exclude_tags_query.rb +63 -60
  32. data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +54 -46
  33. data/lib/acts_as_taggable_on/taggable/tagged_with_query.rb +14 -8
  34. data/lib/acts_as_taggable_on/taggable.rb +14 -14
  35. data/lib/acts_as_taggable_on/tagger.rb +9 -5
  36. data/lib/acts_as_taggable_on/tagging.rb +6 -4
  37. data/lib/acts_as_taggable_on/tags_helper.rb +3 -1
  38. data/lib/acts_as_taggable_on/utils.rb +4 -2
  39. data/lib/acts_as_taggable_on/version.rb +3 -1
  40. data/spec/support/database.rb +36 -26
  41. metadata +13 -14
  42. data/gemfiles/activerecord_5.1.gemfile +0 -21
  43. data/gemfiles/activerecord_5.2.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,263 +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
180
 
165
- def tag_list_on(context)
166
- add_custom_context(context)
167
- tag_list_cache_on(context)
168
- end
181
+ ##
182
+ # Returns all tags of a given context
183
+ def all_tags_on(context)
184
+ tagging_table_name = ActsAsTaggableOn::Tagging.table_name
169
185
 
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)
186
+ opts = ["#{tagging_table_name}.context = ?", context.to_s]
187
+ scope = base_tags.where(opts)
173
188
 
174
- instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(all_tags_on(context).map(&:name)).freeze)
175
- end
176
-
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 taggable_tenant
218
- if self.class.tenant_column
219
- public_send(self.class.tenant_column)
233
+ super(*args)
220
234
  end
221
- end
222
235
 
223
- def reload(*args)
224
- self.class.tag_types.each do |context|
225
- instance_variable_set("@#{context.to_s.singularize}_list", nil)
226
- instance_variable_set("@all_#{context.to_s.singularize}_list", nil)
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)
227
240
  end
228
241
 
229
- super(*args)
230
- end
231
-
232
- ##
233
- # Find existing tags or create non-existing tags
234
- def load_tags(tag_list)
235
- ActsAsTaggableOn::Tag.find_or_create_all_with_like_by_name(tag_list)
236
- end
242
+ def save_tags
243
+ tagging_contexts.each do |context|
244
+ next unless tag_list_cache_set_on(context)
237
245
 
238
- def save_tags
239
- tagging_contexts.each do |context|
240
- next unless tag_list_cache_set_on(context)
241
- # List of currently assigned tag names
242
- tag_list = tag_list_cache_on(context).uniq
246
+ # List of currently assigned tag names
247
+ tag_list = tag_list_cache_on(context).uniq
243
248
 
244
- # Find existing tags or create non-existing tags:
245
- 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)
246
251
 
247
- # Tag objects for currently assigned tags
248
- current_tags = tags_on(context)
252
+ # Tag objects for currently assigned tags
253
+ current_tags = tags_on(context)
249
254
 
250
- # Tag maintenance based on whether preserving the created order of tags
251
- if self.class.preserve_tag_order?
252
- 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?
253
259
 
254
- shared_tags = current_tags & tags
260
+ shared_tags = current_tags & tags
255
261
 
256
- if shared_tags.any? && tags[0...shared_tags.size] != shared_tags
257
- 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
258
266
 
259
- # Update arrays of tag objects
260
- old_tags |= current_tags[index...current_tags.size]
261
- 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
262
270
 
263
- # Order the array of tag objects to match the tag list
264
- new_tags = tags.map do |t|
265
- new_tags.find { |n| n.name.downcase == t.name.downcase }
266
- 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
267
278
  end
268
- else
269
- # Delete discarded tags and create new tags
270
- old_tags = current_tags - tags
271
- new_tags = tags - current_tags
272
- end
273
279
 
274
- # Destroy old taggings:
275
- if old_tags.present?
276
- taggings.not_owned.by_context(context).where(tag_id: old_tags).destroy_all
277
- end
280
+ # Destroy old taggings:
281
+ taggings.not_owned.by_context(context).where(tag_id: old_tags).destroy_all if old_tags.present?
278
282
 
279
- # Create new taggings:
280
- new_tags.each do |tag|
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)
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
285
290
  end
286
291
  end
287
- end
288
292
 
289
- true
290
- end
293
+ true
294
+ end
291
295
 
292
- private
296
+ private
293
297
 
294
- def ensure_included_cache_methods!
295
- self.class.columns
296
- end
298
+ def ensure_included_cache_methods!
299
+ self.class.columns
300
+ end
297
301
 
298
- # Filters the tag lists from the attribute names.
299
- def attributes_for_update(attribute_names)
300
- tag_lists = tag_types.map {|tags_type| "#{tags_type.to_s.singularize}_list"}
301
- super.delete_if {|attr| tag_lists.include? attr }
302
- 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
303
307
 
304
- # Filters the tag lists from the attribute names.
305
- def attributes_for_create(attribute_names)
306
- tag_lists = tag_types.map {|tags_type| "#{tags_type.to_s.singularize}_list"}
307
- super.delete_if {|attr| tag_lists.include? attr }
308
- 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
309
313
 
310
- ##
311
- # Override this hook if you wish to subclass {ActsAsTaggableOn::Tag} --
312
- # context is provided so that you may conditionally use a Tag subclass
313
- # only for some contexts.
314
- #
315
- # @example Custom Tag class for one context
316
- # class Company < ActiveRecord::Base
317
- # acts_as_taggable_on :markets, :locations
318
- #
319
- # def find_or_create_tags_from_list_with_context(tag_list, context)
320
- # if context.to_sym == :markets
321
- # MarketTag.find_or_create_all_with_like_by_name(tag_list)
322
- # else
323
- # super
324
- # end
325
- # end
326
- #
327
- # @param [Array<String>] tag_list Tags to find or create
328
- # @param [Symbol] context The tag context for the tag_list
329
- def find_or_create_tags_from_list_with_context(tag_list, _context)
330
- 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
331
336
  end
332
337
  end
333
338
  end
334
-