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.
- checksums.yaml +4 -4
- data/.travis.yml +21 -1
- data/Appraisals +0 -4
- data/CHANGELOG.md +27 -15
- data/README.md +18 -11
- data/acts-as-taggable-on.gemspec +0 -2
- data/db/migrate/4_add_missing_taggable_index.rb +9 -0
- data/gemfiles/activerecord_3.2.gemfile +0 -1
- data/gemfiles/activerecord_4.0.gemfile +0 -1
- data/gemfiles/activerecord_4.1.gemfile +0 -1
- data/gemfiles/activerecord_edge.gemfile +0 -1
- data/lib/acts-as-taggable-on.rb +28 -20
- data/lib/acts_as_taggable_on/tag.rb +1 -1
- data/lib/acts_as_taggable_on/tag_list.rb +15 -78
- data/lib/acts_as_taggable_on/tag_list_parser.rb +78 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/cache.rb +1 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/collection.rb +2 -0
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/core.rb +68 -44
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/ownership.rb +1 -1
- data/lib/acts_as_taggable_on/{acts_as_taggable_on → taggable}/related.rb +6 -6
- data/lib/acts_as_taggable_on/taggable.rb +7 -8
- data/lib/acts_as_taggable_on/tagging.rb +7 -1
- data/lib/acts_as_taggable_on/tags_helper.rb +2 -2
- data/lib/acts_as_taggable_on/utils.rb +26 -50
- data/lib/acts_as_taggable_on/version.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +1 -0
- data/spec/acts_as_taggable_on/caching_spec.rb +1 -0
- data/spec/acts_as_taggable_on/related_spec.rb +1 -0
- data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +1 -0
- data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
- data/spec/acts_as_taggable_on/tag_list_spec.rb +3 -40
- data/spec/acts_as_taggable_on/tag_spec.rb +34 -38
- data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
- data/spec/acts_as_taggable_on/taggable_spec.rb +84 -164
- data/spec/acts_as_taggable_on/tagger_spec.rb +1 -1
- data/spec/acts_as_taggable_on/tagging_spec.rb +37 -1
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -0
- data/spec/acts_as_taggable_on/utils_spec.rb +6 -11
- data/spec/internal/app/models/cached_model_with_array.rb +1 -1
- data/spec/internal/app/models/models.rb +2 -2
- data/spec/internal/db/schema.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/0-helpers.rb +32 -0
- data/spec/support/database_cleaner.rb +4 -0
- metadata +17 -47
- data/spec/bm.rb +0 -52
- data/spec/schema.rb +0 -82
- /data/lib/acts_as_taggable_on/{acts_as_taggable_on/compatibility.rb → compatibility.rb} +0 -0
- /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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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::
|
|
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 <<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
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 <<
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
|
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 <<
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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::
|
|
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::
|
|
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)
|
|
328
|
-
|
|
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
|
-
|
|
409
|
+
taggings.not_owned.by_context(context).destroy_all(tag_id: old_tags)
|
|
386
410
|
end
|
|
387
411
|
|
|
388
412
|
# Create new taggings:
|
|
@@ -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
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
96
|
-
include
|
|
97
|
-
include
|
|
98
|
-
include
|
|
99
|
-
include
|
|
100
|
-
include
|
|
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(&:
|
|
7
|
+
max_count = tags.sort_by(&:taggings_count).last.taggings_count.to_f
|
|
8
8
|
|
|
9
9
|
tags.each do |tag|
|
|
10
|
-
index = ((tag.
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
20
|
+
def sha_prefix(string)
|
|
21
|
+
Digest::SHA1.hexdigest("#{string}#{rand}")[0..6]
|
|
22
|
+
end
|
|
48
23
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
24
|
+
def active_record4?
|
|
25
|
+
::ActiveRecord::VERSION::MAJOR == 4
|
|
26
|
+
end
|
|
52
27
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
28
|
+
def like_operator
|
|
29
|
+
using_postgresql? ? 'ILIKE' : 'LIKE'
|
|
30
|
+
end
|
|
56
31
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
@@ -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
|
-
|
|
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
|