acts-as-taggable-on 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -11
  3. data/Appraisals +7 -13
  4. data/CHANGELOG.md +82 -0
  5. data/CONTRIBUTING.md +13 -0
  6. data/Gemfile +1 -1
  7. data/README.md +42 -10
  8. data/acts-as-taggable-on.gemspec +2 -2
  9. data/db/migrate/1_acts_as_taggable_on_migration.rb +6 -1
  10. data/db/migrate/2_add_missing_unique_indices.rb +6 -1
  11. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +6 -1
  12. data/db/migrate/4_add_missing_taggable_index.rb +6 -1
  13. data/db/migrate/5_change_collation_for_tag_names.rb +6 -1
  14. data/db/migrate/6_add_missing_indexes_on_taggings.rb +22 -0
  15. data/gemfiles/activerecord_4.2.gemfile +3 -3
  16. data/gemfiles/activerecord_5.0.gemfile +3 -3
  17. data/gemfiles/{activerecord_4.0.gemfile → activerecord_5.1.gemfile} +3 -4
  18. data/lib/acts_as_taggable_on/tag.rb +10 -7
  19. data/lib/acts_as_taggable_on/tag_list.rb +1 -0
  20. data/lib/acts_as_taggable_on/taggable/core.rb +10 -157
  21. data/lib/acts_as_taggable_on/taggable/ownership.rb +16 -5
  22. data/lib/acts_as_taggable_on/taggable/tagged_with_query.rb +16 -0
  23. data/lib/acts_as_taggable_on/taggable/tagged_with_query/all_tags_query.rb +113 -0
  24. data/lib/acts_as_taggable_on/taggable/tagged_with_query/any_tags_query.rb +75 -0
  25. data/lib/acts_as_taggable_on/taggable/tagged_with_query/exclude_tags_query.rb +82 -0
  26. data/lib/acts_as_taggable_on/taggable/tagged_with_query/query_base.rb +61 -0
  27. data/lib/acts_as_taggable_on/tagger.rb +2 -2
  28. data/lib/acts_as_taggable_on/tagging.rb +3 -2
  29. data/lib/acts_as_taggable_on/version.rb +1 -1
  30. data/spec/acts_as_taggable_on/caching_spec.rb +18 -0
  31. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +16 -1
  32. data/spec/acts_as_taggable_on/taggable_spec.rb +1 -1
  33. data/spec/internal/db/schema.rb +3 -0
  34. metadata +13 -9
  35. data/db/migrate/6_add_missing_indexes.rb +0 -12
  36. data/gemfiles/activerecord_4.1.gemfile +0 -16
@@ -41,6 +41,7 @@ module ActsAsTaggableOn
41
41
  # Appends the elements of +other_tag_list+ to +self+.
42
42
  def concat(other_tag_list)
43
43
  super(other_tag_list).send(:clean!)
44
+ self
44
45
  end
45
46
 
46
47
  ##
@@ -1,3 +1,5 @@
1
+ require_relative 'tagged_with_query'
2
+
1
3
  module ActsAsTaggableOn::Taggable
2
4
  module Core
3
5
  def self.included(base)
@@ -25,11 +27,11 @@ module ActsAsTaggableOn::Taggable
25
27
  # the associations tag_taggings & tags are always returned in created order
26
28
  has_many context_taggings, -> { includes(:tag).order(taggings_order).where(context: tags_type) },
27
29
  as: :taggable,
28
- class_name: ActsAsTaggableOn::Tagging,
30
+ class_name: 'ActsAsTaggableOn::Tagging',
29
31
  dependent: :destroy
30
32
 
31
33
  has_many context_tags, -> { order(taggings_order) },
32
- class_name: ActsAsTaggableOn::Tag,
34
+ class_name: 'ActsAsTaggableOn::Tag',
33
35
  through: context_taggings,
34
36
  source: :tag
35
37
  end
@@ -88,169 +90,16 @@ module ActsAsTaggableOn::Taggable
88
90
 
89
91
  return empty_result if tag_list.empty?
90
92
 
91
- joins = []
92
- conditions = []
93
- having = []
94
- select_clause = []
95
- order_by = []
96
-
97
- context = options.delete(:on)
98
- owned_by = options.delete(:owned_by)
99
- alias_base_name = undecorated_table_name.gsub('.', '_')
100
- # FIXME use ActiveRecord's connection quote_column_name
101
- quote = ActsAsTaggableOn::Utils.using_postgresql? ? '"' : ''
102
-
103
- if options.delete(:exclude)
104
- if options.delete(:wild)
105
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{ActsAsTaggableOn::Utils.like_operator} ? ESCAPE '!'", "%#{ActsAsTaggableOn::Utils.escape_like(t)}%"]) }.join(' OR ')
106
- else
107
- tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{ActsAsTaggableOn::Utils.like_operator} ?", t]) }.join(' OR ')
108
- end
109
-
110
- conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)})"
111
-
112
- if owned_by
113
- joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
114
- " ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
115
- " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" +
116
- " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{quote_value(owned_by.id, nil)}" +
117
- " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
118
-
119
- joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
120
- joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
121
- end
122
-
123
- elsif any = options.delete(:any)
124
- # get tags, drop out if nothing returned (we need at least one)
125
- tags = if options.delete(:wild)
126
- ActsAsTaggableOn::Tag.named_like_any(tag_list)
127
- else
128
- ActsAsTaggableOn::Tag.named_any(tag_list)
129
- end
130
-
131
- return empty_result if tags.length == 0
132
-
133
- # setup taggings alias so we can chain, ex: items_locations_taggings_awesome_cool_123
134
- # avoid ambiguous column name
135
- taggings_context = context ? "_#{context}" : ''
136
-
137
- taggings_alias = adjust_taggings_alias(
138
- "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tags.map(&:name).join('_'))}"
139
- )
140
-
141
- tagging_cond = "#{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
142
- " WHERE #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
143
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
144
-
145
- tagging_cond << " AND " + sanitize_sql(["#{taggings_alias}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
146
- tagging_cond << " AND " + sanitize_sql(["#{taggings_alias}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
147
-
148
- tagging_cond << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
149
-
150
- # don't need to sanitize sql, map all ids and join with OR logic
151
- tag_ids = tags.map { |t| quote_value(t.id, nil) }.join(', ')
152
- tagging_cond << " AND #{taggings_alias}.tag_id in (#{tag_ids})"
153
- select_clause << " #{table_name}.*" unless context and tag_types.one?
154
-
155
- if owned_by
156
- tagging_cond << ' AND ' +
157
- sanitize_sql([
158
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
159
- owned_by.id,
160
- owned_by.class.base_class.to_s
161
- ])
162
- end
163
-
164
- conditions << "EXISTS (SELECT 1 FROM #{tagging_cond})"
165
- if options.delete(:order_by_matching_tag_count)
166
- order_by << "(SELECT count(*) FROM #{tagging_cond}) desc"
167
- end
168
- else
169
- tags = ActsAsTaggableOn::Tag.named_any(tag_list)
170
-
171
- return empty_result unless tags.length == tag_list.length
172
-
173
- tags.each do |tag|
174
- taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tag.name)}")
175
- tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" \
176
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
177
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}" +
178
- " AND #{taggings_alias}.tag_id = #{quote_value(tag.id, nil)}"
179
-
180
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
181
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
182
-
183
- tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
184
-
185
- if owned_by
186
- tagging_join << ' AND ' +
187
- sanitize_sql([
188
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
189
- owned_by.id,
190
- owned_by.class.base_class.to_s
191
- ])
192
- end
193
-
194
- joins << tagging_join
195
- end
196
- end
197
-
198
- group ||= [] # Rails interprets this as a no-op in the group() call below
199
- if options.delete(:order_by_matching_tag_count)
200
- select_clause << "#{table_name}.*, COUNT(#{taggings_alias}.tag_id) AS #{taggings_alias}_count"
201
- group_columns = ActsAsTaggableOn::Utils.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
202
- group = group_columns
203
- order_by << "#{taggings_alias}_count DESC"
204
-
205
- elsif options.delete(:match_all)
206
- taggings_alias, _ = adjust_taggings_alias("#{alias_base_name}_taggings_group"), "#{alias_base_name}_tags_group"
207
- joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" \
208
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" \
209
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
210
-
211
- joins << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
212
- joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
213
- joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
214
-
215
- group_columns = ActsAsTaggableOn::Utils.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
216
- group = group_columns
217
- having = "COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
218
- end
219
-
220
- order_by << options[:order] if options[:order].present?
221
-
222
- query = self
223
- query = self.select(select_clause.join(',')) unless select_clause.empty?
224
- query.joins(joins.join(' '))
225
- .where(conditions.join(' AND '))
226
- .group(group)
227
- .having(having)
228
- .order(order_by.join(', '))
229
- .readonly(false)
93
+ ::ActsAsTaggableOn::Taggable::TaggedWithQuery.build(self, ActsAsTaggableOn::Tag, ActsAsTaggableOn::Tagging, tag_list, options)
230
94
  end
231
95
 
232
96
  def is_taggable?
233
97
  true
234
98
  end
235
99
 
236
- def adjust_taggings_alias(taggings_alias)
237
- if taggings_alias.size > 75
238
- taggings_alias = 'taggings_alias_' + Digest::SHA1.hexdigest(taggings_alias)
239
- end
240
- taggings_alias
241
- end
242
-
243
100
  def taggable_mixin
244
101
  @taggable_mixin ||= Module.new
245
102
  end
246
-
247
- private
248
-
249
- # Rails 5 has merged sanitize and quote_value
250
- # See https://github.com/rails/rails/blob/master/activerecord/lib/active_record/sanitization.rb#L10
251
- def quote_value(value, column = nil)
252
- ActsAsTaggableOn::Utils.active_record5? ? super(value) : super(value, column)
253
- end
254
103
  end
255
104
 
256
105
  # all column names are necessary for PostgreSQL group clause
@@ -283,7 +132,7 @@ module ActsAsTaggableOn::Taggable
283
132
  variable_name = "@#{context.to_s.singularize}_list"
284
133
  if instance_variable_get(variable_name)
285
134
  instance_variable_get(variable_name)
286
- elsif cached_tag_list_on(context) && self.class.caching_tag_list_on?(context)
135
+ elsif cached_tag_list_on(context) && ensure_included_cache_methods! && self.class.caching_tag_list_on?(context)
287
136
  instance_variable_set(variable_name, ActsAsTaggableOn.default_parser.new(cached_tag_list_on(context)).parse)
288
137
  else
289
138
  instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
@@ -426,6 +275,10 @@ module ActsAsTaggableOn::Taggable
426
275
 
427
276
  private
428
277
 
278
+ def ensure_included_cache_methods!
279
+ self.class.columns
280
+ end
281
+
429
282
  # Filters the tag lists from the attribute names.
430
283
  def attributes_for_update(attribute_names)
431
284
  tag_lists = tag_types.map {|tags_type| "#{tags_type.to_s.singularize}_list"}
@@ -27,13 +27,16 @@ module ActsAsTaggableOn::Taggable
27
27
  end
28
28
  end
29
29
 
30
- def owner_tags_on(owner, context)
30
+ def owner_tags(owner)
31
31
  if owner.nil?
32
- scope = base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ?), context.to_s])
32
+ scope = base_tags
33
33
  else
34
- scope = base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND
35
- #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = ? AND
36
- #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.base_class.to_s])
34
+ scope = base_tags.where(
35
+ "#{ActsAsTaggableOn::Tagging.table_name}" => {
36
+ tagger_id: owner.id,
37
+ tagger_type: owner.class.base_class.to_s
38
+ }
39
+ )
37
40
  end
38
41
 
39
42
  # when preserving tag order, return tags in created order
@@ -45,6 +48,14 @@ module ActsAsTaggableOn::Taggable
45
48
  end
46
49
  end
47
50
 
51
+ def owner_tags_on(owner, context)
52
+ scope = owner_tags(owner).where(
53
+ "#{ActsAsTaggableOn::Tagging.table_name}" => {
54
+ context: context
55
+ }
56
+ )
57
+ end
58
+
48
59
  def cached_owned_tag_list_on(context)
49
60
  variable_name = "@owned_#{context}_list"
50
61
  (instance_variable_defined?(variable_name) && instance_variable_get(variable_name)) || instance_variable_set(variable_name, {})
@@ -0,0 +1,16 @@
1
+ require_relative 'tagged_with_query/query_base'
2
+ require_relative 'tagged_with_query/exclude_tags_query'
3
+ require_relative 'tagged_with_query/any_tags_query'
4
+ require_relative 'tagged_with_query/all_tags_query'
5
+
6
+ module ActsAsTaggableOn::Taggable::TaggedWithQuery
7
+ def self.build(taggable_model, tag_model, tagging_model, tag_list, options)
8
+ if options[:exclude].present?
9
+ ExcludeTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
10
+ elsif options[:any].present?
11
+ AnyTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
12
+ else
13
+ AllTagsQuery.new(taggable_model, tag_model, tagging_model, tag_list, options).build
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,113 @@
1
+ module ActsAsTaggableOn::Taggable::TaggedWithQuery
2
+ class AllTagsQuery < QueryBase
3
+ def build
4
+ taggable_model.joins(each_tag_in_list)
5
+ .group(by_taggable)
6
+ .having(tags_that_matches_count)
7
+ .order(order_conditions)
8
+ .readonly(false)
9
+ end
10
+
11
+ private
12
+
13
+ def each_tag_in_list
14
+ arel_join = taggable_arel_table
15
+
16
+ tag_list.each do |tag|
17
+ tagging_alias = tagging_arel_table.alias(tagging_alias(tag))
18
+ arel_join = arel_join
19
+ .join(tagging_alias)
20
+ .on(on_conditions(tag, tagging_alias))
21
+ end
22
+
23
+ if options[:match_all].present?
24
+ arel_join = arel_join
25
+ .join(tagging_arel_table, Arel::Nodes::OuterJoin)
26
+ .on(
27
+ match_all_on_conditions
28
+ )
29
+ end
30
+
31
+ return arel_join.join_sources
32
+ end
33
+
34
+ def on_conditions(tag, tagging_alias)
35
+ on_condition = tagging_alias[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
36
+ .and(tagging_alias[:taggable_type].eq(taggable_model.base_class.name))
37
+ .and(
38
+ tagging_alias[:tag_id].in(
39
+ tag_arel_table.project(tag_arel_table[:id]).where(tag_match_type(tag))
40
+ )
41
+ )
42
+
43
+ if options[:start_at].present?
44
+ on_condition = on_condition.and(tagging_alias[:created_at].gteq(options[:start_at]))
45
+ end
46
+
47
+ if options[:end_at].present?
48
+ on_condition = on_condition.and(tagging_alias[:created_at].lteq(options[:end_at]))
49
+ end
50
+
51
+ if options[:on].present?
52
+ on_condition = on_condition.and(tagging_alias[:context].lteq(options[:on]))
53
+ end
54
+
55
+ if (owner = options[:owned_by]).present?
56
+ owner_table = owner.class.base_class.arel_table
57
+
58
+ on_condition = on_condition.and(tagging_alias[:tagger_id].eq(owner.id))
59
+ .and(tagging_alias[:tagger_type].eq(owner.class.base_class.to_s))
60
+ end
61
+
62
+ on_condition
63
+ end
64
+
65
+ def match_all_on_conditions
66
+ on_condition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
67
+ .and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
68
+
69
+ if options[:start_at].present?
70
+ on_condition = on_condition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
71
+ end
72
+
73
+ if options[:end_at].present?
74
+ on_condition = on_condition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
75
+ end
76
+
77
+ if options[:on].present?
78
+ on_condition = on_condition.and(tagging_arel_table[:context].lteq(options[:on]))
79
+ end
80
+
81
+ on_condition
82
+ end
83
+
84
+ def by_taggable
85
+ return [] unless options[:match_all].present?
86
+
87
+ taggable_arel_table[taggable_model.primary_key]
88
+ end
89
+
90
+ def tags_that_matches_count
91
+ return [] unless options[:match_all].present?
92
+
93
+ taggable_model.find_by_sql(tag_arel_table.project(Arel.star.count).where(tags_match_type).to_sql)
94
+
95
+ tagging_arel_table[:taggable_id].count.eq(
96
+ tag_arel_table.project(Arel.star.count).where(tags_match_type)
97
+ )
98
+ end
99
+
100
+ def order_conditions
101
+ order_by = []
102
+ order_by << tagging_arel_table.project(tagging_arel_table[Arel.star].count.as('taggings_count')).order('taggings_count DESC').to_sql if options[:order_by_matching_tag_count].present? && options[:match_all].blank?
103
+
104
+ order_by << options[:order] if options[:order].present?
105
+ order_by.join(', ')
106
+ end
107
+
108
+ def tagging_alias(tag)
109
+ alias_base_name = taggable_model.base_class.name.downcase
110
+ adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tag)}")
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,75 @@
1
+ module ActsAsTaggableOn::Taggable::TaggedWithQuery
2
+ class AnyTagsQuery < QueryBase
3
+ def build
4
+ taggable_model.select(all_fields)
5
+ .where(model_has_at_least_one_tag)
6
+ .order(order_conditions)
7
+ .readonly(false)
8
+ end
9
+
10
+ private
11
+
12
+ def all_fields
13
+ taggable_arel_table[Arel.star]
14
+ end
15
+
16
+ def model_has_at_least_one_tag
17
+ tagging_alias = tagging_arel_table.alias(alias_name(tag_list))
18
+
19
+
20
+ tagging_arel_table.project(Arel.star).where(at_least_one_tag).exists
21
+ end
22
+
23
+ def at_least_one_tag
24
+ exists_contition = tagging_arel_table[:taggable_id].eq(taggable_arel_table[taggable_model.primary_key])
25
+ .and(tagging_arel_table[:taggable_type].eq(taggable_model.base_class.name))
26
+ .and(
27
+ tagging_arel_table[:tag_id].in(
28
+ tag_arel_table.project(tag_arel_table[:id]).where(tags_match_type)
29
+ )
30
+ )
31
+
32
+ if options[:start_at].present?
33
+ exists_contition = exists_contition.and(tagging_arel_table[:created_at].gteq(options[:start_at]))
34
+ end
35
+
36
+ if options[:end_at].present?
37
+ exists_contition = exists_contition.and(tagging_arel_table[:created_at].lteq(options[:end_at]))
38
+ end
39
+
40
+ if options[:on].present?
41
+ exists_contition = exists_contition.and(tagging_arel_table[:context].lteq(options[:on]))
42
+ end
43
+
44
+ if (owner = options[:owned_by]).present?
45
+ owner_table = owner.class.base_class.arel_table
46
+
47
+ exists_contition = exists_contition.and(tagging_arel_table[:tagger_id].eq(owner.id))
48
+ .and(tagging_arel_table[:tagger_type].eq(owner.class.base_class.to_s))
49
+ end
50
+
51
+ exists_contition
52
+ end
53
+
54
+ def order_conditions
55
+ order_by = []
56
+ if options[:order_by_matching_tag_count].present?
57
+ order_by << "(SELECT count(*) FROM #{tagging_model.table_name} WHERE #{at_least_one_tag.to_sql}) desc"
58
+ end
59
+
60
+ order_by << options[:order] if options[:order].present?
61
+ order_by.join(', ')
62
+ end
63
+
64
+ def alias_name(tag_list)
65
+ alias_base_name = taggable_model.base_class.name.downcase
66
+ taggings_context = options[:on] ? "_#{options[:on]}" : ''
67
+
68
+ taggings_alias = adjust_taggings_alias(
69
+ "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tag_list.join('_'))}"
70
+ )
71
+
72
+ taggings_alias
73
+ end
74
+ end
75
+ end