acts-as-taggable-on 3.2.1 → 3.3.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +21 -1
  3. data/Appraisals +0 -4
  4. data/CHANGELOG.md +27 -15
  5. data/README.md +18 -11
  6. data/acts-as-taggable-on.gemspec +0 -2
  7. data/db/migrate/4_add_missing_taggable_index.rb +9 -0
  8. data/gemfiles/activerecord_3.2.gemfile +0 -1
  9. data/gemfiles/activerecord_4.0.gemfile +0 -1
  10. data/gemfiles/activerecord_4.1.gemfile +0 -1
  11. data/gemfiles/activerecord_edge.gemfile +0 -1
  12. data/lib/acts-as-taggable-on.rb +28 -20
  13. data/lib/acts_as_taggable_on/tag.rb +1 -1
  14. data/lib/acts_as_taggable_on/tag_list.rb +15 -78
  15. data/lib/acts_as_taggable_on/tag_list_parser.rb +78 -0
  16. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/cache.rb +1 -0
  17. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/collection.rb +2 -0
  18. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/core.rb +68 -44
  19. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/ownership.rb +1 -1
  20. data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/related.rb +6 -6
  21. data/lib/acts_as_taggable_on/taggable.rb +7 -8
  22. data/lib/acts_as_taggable_on/tagging.rb +7 -1
  23. data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
  24. data/lib/acts_as_taggable_on/utils.rb +26 -50
  25. data/lib/acts_as_taggable_on/version.rb +1 -1
  26. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +1 -1
  27. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -0
  28. data/spec/acts_as_taggable_on/caching_spec.rb +1 -0
  29. data/spec/acts_as_taggable_on/related_spec.rb +1 -0
  30. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +1 -0
  31. data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
  32. data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -40
  33. data/spec/acts_as_taggable_on/tag_spec.rb +34 -38
  34. data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
  35. data/spec/acts_as_taggable_on/taggable_spec.rb +84 -164
  36. data/spec/acts_as_taggable_on/tagger_spec.rb +1 -1
  37. data/spec/acts_as_taggable_on/tagging_spec.rb +37 -1
  38. data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -0
  39. data/spec/acts_as_taggable_on/utils_spec.rb +6 -11
  40. data/spec/internal/app/models/cached_model_with_array.rb +1 -1
  41. data/spec/internal/app/models/models.rb +2 -2
  42. data/spec/internal/db/schema.rb +2 -2
  43. data/spec/spec_helper.rb +0 -1
  44. data/spec/support/0-helpers.rb +32 -0
  45. data/spec/support/database_cleaner.rb +4 -0
  46. metadata +17 -47
  47. data/spec/bm.rb +0 -52
  48. data/spec/schema.rb +0 -82
  49. /data/lib/acts_as_taggable_on/{acts_as_taggable_on/compatibility.rb → compatibility.rb} +0 -0
  50. /data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/dirty.rb +0 -0
@@ -24,16 +24,16 @@ module ActsAsTaggableOn::Taggable
24
24
  # when preserving tag order, include order option so that for a 'tags' context
25
25
  # the associations tag_taggings & tags are always returned in created order
26
26
  has_many_with_taggable_compatibility context_taggings, as: :taggable,
27
- dependent: :destroy,
28
- class_name: 'ActsAsTaggableOn::Tagging',
29
- order: taggings_order,
30
- conditions: ["#{ActsAsTaggableOn::Tagging.table_name}.context = (?)", tags_type],
31
- include: :tag
27
+ dependent: :destroy,
28
+ class_name: 'ActsAsTaggableOn::Tagging',
29
+ order: taggings_order,
30
+ conditions: ["#{ActsAsTaggableOn::Tagging.table_name}.context = (?)", tags_type],
31
+ include: :tag
32
32
 
33
33
  has_many_with_taggable_compatibility context_tags, through: context_taggings,
34
- source: :tag,
35
- class_name: 'ActsAsTaggableOn::Tag',
36
- order: taggings_order
34
+ source: :tag,
35
+ class_name: 'ActsAsTaggableOn::Tag',
36
+ order: taggings_order
37
37
 
38
38
  end
39
39
 
@@ -73,6 +73,8 @@ module ActsAsTaggableOn::Taggable
73
73
  # * <tt>:order_by_matching_tag_count</tt> - if set to true and used with :any, sort by objects matching the most tags, descending
74
74
  # * <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
75
75
  # * <tt>:owned_by</tt> - return objects that are *ONLY* owned by the owner
76
+ # * <tt>:start_at</tt> - Restrict the tags to those created after a certain time
77
+ # * <tt>:end_at</tt> - Restrict the tags to those created before a certain time
76
78
  #
77
79
  # Example:
78
80
  # User.tagged_with("awesome", "cool") # Users that are tagged with awesome and cool
@@ -81,8 +83,10 @@ module ActsAsTaggableOn::Taggable
81
83
  # User.tagged_with("awesome", "cool", :any => true, :order_by_matching_tag_count => true) # Sort by users who match the most tags, descending
82
84
  # User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
83
85
  # User.tagged_with("awesome", "cool", :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
86
+ # 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
84
87
  def tagged_with(tags, options = {})
85
- tag_list = ActsAsTaggableOn::TagList.from(tags)
88
+ tag_list = ActsAsTaggableOn::TagListParser.parse(tags)
89
+ options = options.dup
86
90
  empty_result = where('1 = 0')
87
91
 
88
92
  return empty_result if tag_list.empty?
@@ -108,14 +112,17 @@ module ActsAsTaggableOn::Taggable
108
112
  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)})"
109
113
 
110
114
  if owned_by
111
- joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" \
112
- " ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" \
113
- " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" \
114
- " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{quote_value(owned_by.id, nil)}" \
115
- " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
115
+ joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
116
+ " ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
117
+ " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" +
118
+ " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{quote_value(owned_by.id, nil)}" +
119
+ " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
120
+
121
+ joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
122
+ joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
116
123
  end
117
124
 
118
- elsif options.delete(:any)
125
+ elsif any = options.delete(:any)
119
126
  # get tags, drop out if nothing returned (we need at least one)
120
127
  tags = if options.delete(:wild)
121
128
  ActsAsTaggableOn::Tag.named_like_any(tag_list)
@@ -130,49 +137,59 @@ module ActsAsTaggableOn::Taggable
130
137
  taggings_context = context ? "_#{context}" : ''
131
138
 
132
139
  taggings_alias = adjust_taggings_alias(
133
- "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{sha_prefix(tags.map(&:name).join('_'))}"
140
+ "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tags.map(&:name).join('_'))}"
134
141
  )
135
142
 
136
- tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" \
137
- " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" \
138
- " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
139
- tagging_join << ' AND ' + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
143
+ tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
144
+ " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
145
+ " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
146
+
147
+ tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
148
+ tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
149
+
150
+ tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
140
151
 
141
152
  # don't need to sanitize sql, map all ids and join with OR logic
142
153
  conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{quote_value(t.id, nil)}" }.join(' OR ')
143
- select_clause = " #{table_name}.*" unless context and tag_types.one?
154
+ select_clause << " #{table_name}.*" unless context and tag_types.one?
144
155
 
145
156
  if owned_by
146
157
  tagging_join << ' AND ' +
147
158
  sanitize_sql([
148
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
149
- owned_by.id,
150
- owned_by.class.base_class.to_s
159
+ "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
160
+ owned_by.id,
161
+ owned_by.class.base_class.to_s
151
162
  ])
152
163
  end
153
164
 
154
165
  joins << tagging_join
155
- group = "#{table_name}.#{primary_key}"
166
+ unless any == 'distinct' # Fix issue #544
167
+ group = "#{table_name}.#{primary_key}"
168
+ select_clause << group
169
+ end
156
170
  else
157
171
  tags = ActsAsTaggableOn::Tag.named_any(tag_list)
158
172
 
159
173
  return empty_result unless tags.length == tag_list.length
160
174
 
161
175
  tags.each do |tag|
162
- taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{sha_prefix(tag.name)}")
176
+ taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{ActsAsTaggableOn::Utils.sha_prefix(tag.name)}")
163
177
  tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" \
164
178
  " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
165
179
  " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}" +
166
180
  " AND #{taggings_alias}.tag_id = #{quote_value(tag.id, nil)}"
167
181
 
168
- tagging_join << ' AND ' + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
182
+ tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
183
+ tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
184
+
185
+ tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
169
186
 
170
187
  if owned_by
171
188
  tagging_join << ' AND ' +
172
189
  sanitize_sql([
173
- "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
174
- owned_by.id,
175
- owned_by.class.base_class.to_s
190
+ "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
191
+ owned_by.id,
192
+ owned_by.class.base_class.to_s
176
193
  ])
177
194
  end
178
195
 
@@ -182,7 +199,7 @@ module ActsAsTaggableOn::Taggable
182
199
 
183
200
  group ||= [] # Rails interprets this as a no-op in the group() call below
184
201
  if options.delete(:order_by_matching_tag_count)
185
- select_clause = "#{table_name}.*, COUNT(#{taggings_alias}.tag_id) AS #{taggings_alias}_count"
202
+ select_clause << "#{table_name}.*, COUNT(#{taggings_alias}.tag_id) AS #{taggings_alias}_count"
186
203
  group_columns = ActsAsTaggableOn::Utils.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
187
204
  group = group_columns
188
205
  order_by << "#{taggings_alias}_count DESC"
@@ -193,7 +210,9 @@ module ActsAsTaggableOn::Taggable
193
210
  " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" \
194
211
  " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
195
212
 
196
- joins << ' AND ' + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
213
+ joins << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
214
+ joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
215
+ joins << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
197
216
 
198
217
  group_columns = ActsAsTaggableOn::Utils.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
199
218
  group = group_columns
@@ -202,13 +221,14 @@ module ActsAsTaggableOn::Taggable
202
221
 
203
222
  order_by << options[:order] if options[:order].present?
204
223
 
205
- request = select(select_clause).
206
- joins(joins.join(' ')).
207
- where(conditions.join(' AND ')).
208
- group(group).
209
- having(having).
210
- order(order_by.join(', ')).
211
- readonly(false)
224
+ query = self
225
+ query = self.select(select_clause.join(',')) unless select_clause.empty?
226
+ query.joins(joins.join(' '))
227
+ .where(conditions.join(' AND '))
228
+ .group(group)
229
+ .having(having)
230
+ .order(order_by.join(', '))
231
+ .readonly(false)
212
232
  end
213
233
 
214
234
  def is_taggable?
@@ -258,7 +278,7 @@ module ActsAsTaggableOn::Taggable
258
278
  if instance_variable_get(variable_name)
259
279
  instance_variable_get(variable_name)
260
280
  elsif cached_tag_list_on(context) && self.class.caching_tag_list_on?(context)
261
- instance_variable_set(variable_name, ActsAsTaggableOn::TagList.from(cached_tag_list_on(context)))
281
+ instance_variable_set(variable_name, ActsAsTaggableOn::TagListParser.parse(cached_tag_list_on(context)))
262
282
  else
263
283
  instance_variable_set(variable_name, ActsAsTaggableOn::TagList.new(tags_on(context).map(&:name)))
264
284
  end
@@ -308,7 +328,7 @@ module ActsAsTaggableOn::Taggable
308
328
  variable_name = "@#{context.to_s.singularize}_list"
309
329
  process_dirty_object(context, new_list) unless custom_contexts.include?(context.to_s)
310
330
 
311
- instance_variable_set(variable_name, ActsAsTaggableOn::TagList.from(new_list))
331
+ instance_variable_set(variable_name, ActsAsTaggableOn::TagListParser.parse(new_list))
312
332
  end
313
333
 
314
334
  def tagging_contexts
@@ -324,8 +344,12 @@ module ActsAsTaggableOn::Taggable
324
344
  old = changed_attributes[attrib]
325
345
  changed_attributes.delete(attrib) if old.to_s == value.to_s
326
346
  else
327
- old = tag_list_on(context).to_s
328
- changed_attributes[attrib] = old if old.to_s != value.to_s
347
+ old = tag_list_on(context)
348
+ if self.class.preserve_tag_order
349
+ changed_attributes[attrib] = old if old.to_s != value.to_s
350
+ else
351
+ changed_attributes[attrib] = old.to_s if old.sort != ActsAsTaggableOn::TagListParser.parse(value).sort
352
+ end
329
353
  end
330
354
  end
331
355
 
@@ -382,7 +406,7 @@ module ActsAsTaggableOn::Taggable
382
406
 
383
407
  # Destroy old taggings:
384
408
  if old_tags.present?
385
- ActsAsTaggableOn::Tagging.destroy_all(tagger_type: nil, tagger_id: nil, context: context.to_s, tag_id: old_tags)
409
+ taggings.not_owned.by_context(context).destroy_all(tag_id: old_tags)
386
410
  end
387
411
 
388
412
  # Create new taggings:
@@ -63,7 +63,7 @@ module ActsAsTaggableOn::Taggable
63
63
 
64
64
  cache = cached_owned_tag_list_on(context)
65
65
 
66
- cache[owner] = ActsAsTaggableOn::TagList.from(new_list)
66
+ cache[owner] = ActsAsTaggableOn::TagListParser.parse(new_list)
67
67
  end
68
68
 
69
69
  def reload(*args)
@@ -41,7 +41,7 @@ module ActsAsTaggableOn::Taggable
41
41
  end
42
42
 
43
43
  def related_tags_for(context, klass, options = {})
44
- tags_to_ignore = Array.wrap(options.delete(:ignore)).map(&:to_s) || []
44
+ tags_to_ignore = Array.wrap(options[:ignore]).map(&:to_s) || []
45
45
  tags_to_find = tags_on(context).map { |t| t.name }.reject { |t| tags_to_ignore.include? t }
46
46
  related_where(klass, ["#{exclude_self(klass, id)} #{klass.table_name}.#{klass.primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.base_class}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key} AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?)", tags_to_find])
47
47
  end
@@ -61,11 +61,11 @@ module ActsAsTaggableOn::Taggable
61
61
  end
62
62
 
63
63
  def related_where(klass, conditions)
64
- klass.select("#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key}) AS count").
65
- from("#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}").
66
- group(group_columns(klass)).
67
- order('count DESC').
68
- where(conditions)
64
+ klass.select("#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key}) AS count")
65
+ .from("#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}")
66
+ .group(group_columns(klass))
67
+ .order('count DESC')
68
+ .where(conditions)
69
69
  end
70
70
  end
71
71
  end
@@ -1,5 +1,6 @@
1
1
  module ActsAsTaggableOn
2
2
  module Taggable
3
+
3
4
  def taggable?
4
5
  false
5
6
  end
@@ -85,19 +86,17 @@ module ActsAsTaggableOn
85
86
  def self.taggable?
86
87
  true
87
88
  end
88
-
89
- extend ActsAsTaggableOn::Utils
90
89
  end
91
90
  end
92
91
 
93
92
  # each of these add context-specific methods and must be
94
93
  # called on each call of taggable_on
95
- include ActsAsTaggableOn::Taggable::Core
96
- include ActsAsTaggableOn::Taggable::Collection
97
- include ActsAsTaggableOn::Taggable::Cache
98
- include ActsAsTaggableOn::Taggable::Ownership
99
- include ActsAsTaggableOn::Taggable::Related
100
- include ActsAsTaggableOn::Taggable::Dirty
94
+ include Core
95
+ include Collection
96
+ include Cache
97
+ include Ownership
98
+ include Related
99
+ include Dirty
101
100
  end
102
101
  end
103
102
  end
@@ -15,6 +15,12 @@ module ActsAsTaggableOn
15
15
  belongs_to :taggable, polymorphic: true
16
16
  belongs_to :tagger, polymorphic: true
17
17
 
18
+ scope :owned_by, ->(owner) { where(tagger: owner) }
19
+ scope :not_owned, -> { where(tagger_id: nil, tagger_type: nil) }
20
+
21
+ scope :by_contexts, ->(contexts = ['tags']) { where(context: contexts) }
22
+ scope :by_context, ->(context= 'tags') { by_contexts(context.to_s) }
23
+
18
24
  validates_presence_of :context
19
25
  validates_presence_of :tag_id
20
26
 
@@ -26,7 +32,7 @@ module ActsAsTaggableOn
26
32
 
27
33
  def remove_unused_tags
28
34
  if ActsAsTaggableOn.remove_unused_tags
29
- tag.destroy if tag.taggings_count.zero?
35
+ tag.destroy if tag.reload.taggings_count.zero?
30
36
  end
31
37
  end
32
38
  end
@@ -4,10 +4,10 @@ module ActsAsTaggableOn
4
4
  def tag_cloud(tags, classes)
5
5
  return [] if tags.empty?
6
6
 
7
- max_count = tags.sort_by(&:count).last.count.to_f
7
+ max_count = tags.sort_by(&:taggings_count).last.taggings_count.to_f
8
8
 
9
9
  tags.each do |tag|
10
- index = ((tag.count / max_count) * (classes.size - 1))
10
+ index = ((tag.taggings_count / max_count) * (classes.size - 1))
11
11
  yield tag, classes[index.nan? ? 0 : index.round]
12
12
  end
13
13
  end
@@ -1,62 +1,38 @@
1
+ # This module is deprecated and will be removed in the incoming versions
2
+
1
3
  module ActsAsTaggableOn
2
4
  module Utils
3
- extend self
4
-
5
- # Use ActsAsTaggableOn::Tag connection
6
- def connection
7
- ActsAsTaggableOn::Tag.connection
8
- end
9
-
10
- def using_postgresql?
11
- connection && connection.adapter_name == 'PostgreSQL'
12
- end
13
-
14
- def postgresql_version
15
- if using_postgresql?
16
- connection.execute("SHOW SERVER_VERSION").first["server_version"].to_f
5
+ class << self
6
+ # Use ActsAsTaggableOn::Tag connection
7
+ def connection
8
+ ActsAsTaggableOn::Tag.connection
17
9
  end
18
- end
19
-
20
- def postgresql_support_json?
21
- postgresql_version >= 9.2
22
- end
23
-
24
- def using_sqlite?
25
- connection && connection.adapter_name == 'SQLite'
26
- end
27
-
28
- def using_mysql?
29
- #We should probably use regex for mysql to support prehistoric adapters
30
- connection && connection.adapter_name == 'Mysql2'
31
- end
32
10
 
33
- def using_case_insensitive_collation?
34
- using_mysql? && connection.collation =~ /_ci\Z/
35
- end
36
-
37
- def supports_concurrency?
38
- !using_sqlite?
39
- end
11
+ def using_postgresql?
12
+ connection && connection.adapter_name == 'PostgreSQL'
13
+ end
40
14
 
41
- def sha_prefix(string)
42
- Digest::SHA1.hexdigest("#{string}#{rand}")[0..6]
43
- end
15
+ def using_mysql?
16
+ #We should probably use regex for mysql to support prehistoric adapters
17
+ connection && connection.adapter_name == 'Mysql2'
18
+ end
44
19
 
45
- def active_record4?
46
- ::ActiveRecord::VERSION::MAJOR == 4
47
- end
20
+ def sha_prefix(string)
21
+ Digest::SHA1.hexdigest("#{string}#{rand}")[0..6]
22
+ end
48
23
 
49
- def active_record42?
50
- active_record4? && ::ActiveRecord::VERSION::MINOR >= 2
51
- end
24
+ def active_record4?
25
+ ::ActiveRecord::VERSION::MAJOR == 4
26
+ end
52
27
 
53
- def like_operator
54
- using_postgresql? ? 'ILIKE' : 'LIKE'
55
- end
28
+ def like_operator
29
+ using_postgresql? ? 'ILIKE' : 'LIKE'
30
+ end
56
31
 
57
- # escape _ and % characters in strings, since these are wildcards in SQL.
58
- def escape_like(str)
59
- str.gsub(/[!%_]/) { |x| '!' + x }
32
+ # escape _ and % characters in strings, since these are wildcards in SQL.
33
+ def escape_like(str)
34
+ str.gsub(/[!%_]/) { |x| '!' + x }
35
+ end
60
36
  end
61
37
  end
62
38
  end
@@ -1,4 +1,4 @@
1
1
  module ActsAsTaggableOn
2
- VERSION = '3.2.1'
2
+ VERSION = '3.3.0'
3
3
  end
4
4
 
@@ -1,4 +1,4 @@
1
- # coding: utf-8
1
+ # -*- encoding : utf-8 -*-
2
2
  require 'spec_helper'
3
3
 
4
4
  describe 'Acts As Taggable On' do
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'spec_helper'
2
3
 
3
4
  describe 'acts_as_tagger' do
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'spec_helper'
2
3
 
3
4
  describe 'Acts As Taggable On' do
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'spec_helper'
2
3
 
3
4
  describe 'Acts As Taggable On' do
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'spec_helper'
2
3
 
3
4
  describe 'Single Table Inheritance' do
@@ -0,0 +1,46 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe ActsAsTaggableOn::TagListParser do
5
+ it '#parse should return empty array if empty array is passed' do
6
+ expect(ActsAsTaggableOn::TagListParser.parse([])).to be_empty
7
+ end
8
+
9
+ describe 'Multiple Delimiter' do
10
+ before do
11
+ @old_delimiter = ActsAsTaggableOn.delimiter
12
+ end
13
+
14
+ after do
15
+ ActsAsTaggableOn.delimiter = @old_delimiter
16
+ end
17
+
18
+ it 'should separate tags by delimiters' do
19
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
20
+ tag_list = ActsAsTaggableOn::TagListParser.parse('cool, data|I have')
21
+ expect(tag_list.to_s).to eq('cool, data, I, have')
22
+ end
23
+
24
+ it 'should escape quote' do
25
+ ActsAsTaggableOn.delimiter = [',', ' ', '\|']
26
+ tag_list = ActsAsTaggableOn::TagListParser.parse "'I have'|cool, data"
27
+ expect(tag_list.to_s).to eq('"I have", cool, data')
28
+
29
+ tag_list = ActsAsTaggableOn::TagListParser.parse '"I, have"|cool, data'
30
+ expect(tag_list.to_s).to eq('"I, have", cool, data')
31
+ end
32
+
33
+ it 'should work for utf8 delimiter and long delimiter' do
34
+ ActsAsTaggableOn.delimiter = [',', '的', '可能是']
35
+ tag_list = ActsAsTaggableOn::TagListParser.parse('我的东西可能是不见了,还好有备份')
36
+ expect(tag_list.to_s).to eq('我, 东西, 不见了, 还好有备份')
37
+ end
38
+
39
+ it 'should work for multiple quoted tags' do
40
+ ActsAsTaggableOn.delimiter = [',']
41
+ tag_list = ActsAsTaggableOn::TagListParser.parse('"Ruby Monsters","eat Katzenzungen"')
42
+ expect(tag_list.to_s).to eq('Ruby Monsters, eat Katzenzungen')
43
+ end
44
+ end
45
+
46
+ end
@@ -1,4 +1,5 @@
1
- # encoding: utf-8
1
+ # -*- encoding : utf-8 -*-
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe ActsAsTaggableOn::TagList do
@@ -7,9 +8,7 @@ describe ActsAsTaggableOn::TagList do
7
8
 
8
9
  it { should be_kind_of Array }
9
10
 
10
- it '#from should return empty array if empty array is passed' do
11
- expect(ActsAsTaggableOn::TagList.from([])).to be_empty
12
- end
11
+
13
12
 
14
13
  describe '#add' do
15
14
  it 'should be able to be add a new tag word' do
@@ -118,41 +117,5 @@ describe ActsAsTaggableOn::TagList do
118
117
 
119
118
  end
120
119
 
121
- describe 'Multiple Delimiter' do
122
- before do
123
- @old_delimiter = ActsAsTaggableOn.delimiter
124
- end
125
-
126
- after do
127
- ActsAsTaggableOn.delimiter = @old_delimiter
128
- end
129
-
130
- it 'should separate tags by delimiters' do
131
- ActsAsTaggableOn.delimiter = [',', ' ', '\|']
132
- tag_list = ActsAsTaggableOn::TagList.from 'cool, data|I have'
133
- expect(tag_list.to_s).to eq('cool, data, I, have')
134
- end
135
-
136
- it 'should escape quote' do
137
- ActsAsTaggableOn.delimiter = [',', ' ', '\|']
138
- tag_list = ActsAsTaggableOn::TagList.from "'I have'|cool, data"
139
- expect(tag_list.to_s).to eq('"I have", cool, data')
140
-
141
- tag_list = ActsAsTaggableOn::TagList.from '"I, have"|cool, data'
142
- expect(tag_list.to_s).to eq('"I, have", cool, data')
143
- end
144
-
145
- it 'should work for utf8 delimiter and long delimiter' do
146
- ActsAsTaggableOn.delimiter = [',', '的', '可能是']
147
- tag_list = ActsAsTaggableOn::TagList.from '我的东西可能是不见了,还好有备份'
148
- expect(tag_list.to_s).to eq('我, 东西, 不见了, 还好有备份')
149
- end
150
-
151
- it 'should work for multiple quoted tags' do
152
- ActsAsTaggableOn.delimiter = [',']
153
- tag_list = ActsAsTaggableOn::TagList.from '"Ruby Monsters","eat Katzenzungen"'
154
- expect(tag_list.to_s).to eq('Ruby Monsters, eat Katzenzungen')
155
- end
156
- end
157
120
 
158
121
  end