acts-as-taggable-on 1.0.13 → 2.0.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.
- data/CHANGELOG +5 -2
- data/Gemfile +6 -0
- data/README.rdoc +61 -31
- data/Rakefile +46 -16
- data/VERSION +1 -1
- data/generators/acts_as_taggable_on_migration/acts_as_taggable_on_migration_generator.rb +7 -0
- data/generators/acts_as_taggable_on_migration/templates/migration.rb +29 -0
- data/lib/acts-as-taggable-on.rb +30 -7
- data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +53 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +98 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +237 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +101 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +64 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +43 -373
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +58 -43
- data/lib/acts_as_taggable_on/compatibility/Gemfile +6 -0
- data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +17 -0
- data/lib/acts_as_taggable_on/tag.rb +47 -8
- data/lib/acts_as_taggable_on/tag_list.rb +45 -45
- data/lib/acts_as_taggable_on/tagging.rb +17 -2
- data/lib/acts_as_taggable_on/tags_helper.rb +8 -2
- data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +31 -0
- data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +28 -0
- data/rails/init.rb +1 -7
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +98 -53
- data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +46 -4
- data/spec/acts_as_taggable_on/tag_list_spec.rb +18 -0
- data/spec/acts_as_taggable_on/tag_spec.rb +66 -13
- data/spec/acts_as_taggable_on/taggable_spec.rb +142 -70
- data/spec/acts_as_taggable_on/tagger_spec.rb +73 -5
- data/spec/acts_as_taggable_on/tagging_spec.rb +18 -3
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +1 -3
- data/spec/bm.rb +52 -0
- data/spec/models.rb +30 -0
- data/spec/schema.rb +13 -2
- data/spec/spec.opts +1 -2
- data/spec/spec_helper.rb +39 -34
- metadata +28 -8
- data/lib/acts_as_taggable_on/group_helper.rb +0 -12
- data/spec/acts_as_taggable_on/group_helper_spec.rb +0 -18
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
module ActsAsTaggableOn::Taggable
|
|
2
|
+
module Core
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.send :include, ActsAsTaggableOn::Taggable::Core::InstanceMethods
|
|
5
|
+
base.extend ActsAsTaggableOn::Taggable::Core::ClassMethods
|
|
6
|
+
|
|
7
|
+
base.class_eval do
|
|
8
|
+
attr_writer :custom_contexts
|
|
9
|
+
after_save :save_tags
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
base.initialize_acts_as_taggable_on_core
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
def initialize_acts_as_taggable_on_core
|
|
17
|
+
tag_types.map(&:to_s).each do |tags_type|
|
|
18
|
+
tag_type = tags_type.to_s.singularize
|
|
19
|
+
context_taggings = "#{tag_type}_taggings".to_sym
|
|
20
|
+
context_tags = tags_type.to_sym
|
|
21
|
+
|
|
22
|
+
class_eval do
|
|
23
|
+
has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "Tagging",
|
|
24
|
+
:conditions => ['#{Tagging.table_name}.tagger_id IS NULL AND #{Tagging.table_name}.context = ?', tags_type]
|
|
25
|
+
has_many context_tags, :through => context_taggings, :source => :tag
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class_eval %(
|
|
29
|
+
def #{tag_type}_list
|
|
30
|
+
tag_list_on('#{tags_type}')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def #{tag_type}_list=(new_tags)
|
|
34
|
+
set_tag_list_on('#{tags_type}', new_tags)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def all_#{tags_type}_list
|
|
38
|
+
all_tags_list_on('#{tags_type}')
|
|
39
|
+
end
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def acts_as_taggable_on(*args)
|
|
45
|
+
super(*args)
|
|
46
|
+
initialize_acts_as_taggable_on_core
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# all column names are necessary for PostgreSQL group clause
|
|
50
|
+
def grouped_column_names_for(object)
|
|
51
|
+
object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Return a scope of objects that are tagged with the specified tags.
|
|
56
|
+
#
|
|
57
|
+
# @param tags The tags that we want to query for
|
|
58
|
+
# @param [Hash] options A hash of options to alter you query:
|
|
59
|
+
# * <tt>:exclude</tt> - if set to true, return objects that are *NOT* tagged with the specified tags
|
|
60
|
+
# * <tt>:any</tt> - if set to true, return objects that are tagged with *ANY* of the specified tags
|
|
61
|
+
# * <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
|
|
62
|
+
#
|
|
63
|
+
# Example:
|
|
64
|
+
# User.tagged_with("awesome", "cool") # Users that are tagged with awesome and cool
|
|
65
|
+
# User.tagged_with("awesome", "cool", :exclude => true) # Users that are not tagged with awesome or cool
|
|
66
|
+
# User.tagged_with("awesome", "cool", :any => true) # Users that are tagged with awesome or cool
|
|
67
|
+
# User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
|
|
68
|
+
def tagged_with(tags, options = {})
|
|
69
|
+
tag_list = TagList.from(tags)
|
|
70
|
+
|
|
71
|
+
return {} if tag_list.empty?
|
|
72
|
+
|
|
73
|
+
joins = []
|
|
74
|
+
conditions = []
|
|
75
|
+
|
|
76
|
+
context = options.delete(:on)
|
|
77
|
+
|
|
78
|
+
if options.delete(:exclude)
|
|
79
|
+
tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
|
|
80
|
+
conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name} JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND (#{tags_conditions}) WHERE #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
|
|
81
|
+
|
|
82
|
+
elsif options.delete(:any)
|
|
83
|
+
tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
|
|
84
|
+
conditions << "#{table_name}.#{primary_key} IN (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name} JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND (#{tags_conditions}) WHERE #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
|
|
85
|
+
|
|
86
|
+
else
|
|
87
|
+
tags = Tag.named_any(tag_list)
|
|
88
|
+
return where("1 = 0") unless tags.length == tag_list.length
|
|
89
|
+
|
|
90
|
+
tags.each do |tag|
|
|
91
|
+
safe_tag = tag.name.gsub(/[^a-zA-Z0-9]/, '')
|
|
92
|
+
prefix = "#{safe_tag}_#{rand(1024)}"
|
|
93
|
+
|
|
94
|
+
taggings_alias = "#{table_name}_taggings_#{prefix}"
|
|
95
|
+
|
|
96
|
+
tagging_join = "JOIN #{Tagging.table_name} #{taggings_alias}" +
|
|
97
|
+
" ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
|
|
98
|
+
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
|
|
99
|
+
" AND #{taggings_alias}.tag_id = #{tag.id}"
|
|
100
|
+
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
|
|
101
|
+
|
|
102
|
+
joins << tagging_join
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"
|
|
107
|
+
|
|
108
|
+
if options.delete(:match_all)
|
|
109
|
+
joins << "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias}" +
|
|
110
|
+
" ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
|
|
111
|
+
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
|
|
112
|
+
|
|
113
|
+
group = "#{grouped_column_names_for(self)} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
scoped(:joins => joins.join(" "),
|
|
118
|
+
:group => group,
|
|
119
|
+
:conditions => conditions.join(" AND "),
|
|
120
|
+
:readonly => false)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def is_taggable?
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
module InstanceMethods
|
|
129
|
+
# all column names are necessary for PostgreSQL group clause
|
|
130
|
+
def grouped_column_names_for(object)
|
|
131
|
+
self.class.grouped_column_names_for(object)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def custom_contexts
|
|
135
|
+
@custom_contexts ||= []
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def is_taggable?
|
|
139
|
+
self.class.is_taggable?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def add_custom_context(value)
|
|
143
|
+
custom_contexts << value.to_s unless custom_contexts.include?(value.to_s) or self.class.tag_types.map(&:to_s).include?(value.to_s)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def cached_tag_list_on(context)
|
|
147
|
+
self["cached_#{context.to_s.singularize}_list"]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def tag_list_cache_set_on(context)
|
|
151
|
+
variable_name = "@#{context.to_s.singularize}_list"
|
|
152
|
+
!instance_variable_get(variable_name).nil?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def tag_list_cache_on(context)
|
|
156
|
+
variable_name = "@#{context.to_s.singularize}_list"
|
|
157
|
+
instance_variable_get(variable_name) || instance_variable_set(variable_name, TagList.new(tags_on(context).map(&:name)))
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def tag_list_on(context)
|
|
161
|
+
add_custom_context(context)
|
|
162
|
+
tag_list_cache_on(context)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def all_tags_list_on(context)
|
|
166
|
+
variable_name = "@all_#{context.to_s.singularize}_list"
|
|
167
|
+
return instance_variable_get(variable_name) if instance_variable_get(variable_name)
|
|
168
|
+
|
|
169
|
+
instance_variable_set(variable_name, TagList.new(all_tags_on(context).map(&:name)).freeze)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
##
|
|
173
|
+
# Returns all tags of a given context
|
|
174
|
+
def all_tags_on(context)
|
|
175
|
+
opts = ["#{Tagging.table_name}.context = ?", context.to_s]
|
|
176
|
+
base_tags.where(opts).order("#{Tagging.table_name}.created_at").group("#{Tagging.table_name}.tag_id").all
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
##
|
|
180
|
+
# Returns all tags that are not owned of a given context
|
|
181
|
+
def tags_on(context)
|
|
182
|
+
base_tags.where(["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]).all
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def set_tag_list_on(context, new_list)
|
|
186
|
+
add_custom_context(context)
|
|
187
|
+
|
|
188
|
+
variable_name = "@#{context.to_s.singularize}_list"
|
|
189
|
+
instance_variable_set(variable_name, TagList.from(new_list))
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def tagging_contexts
|
|
193
|
+
custom_contexts + self.class.tag_types.map(&:to_s)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def reload
|
|
197
|
+
self.class.tag_types.each do |context|
|
|
198
|
+
instance_variable_set("@#{context.to_s.singularize}_list", nil)
|
|
199
|
+
instance_variable_set("@all_#{context.to_s.singularize}_list", nil)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
super
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def save_tags
|
|
206
|
+
tagging_contexts.each do |context|
|
|
207
|
+
next unless tag_list_cache_set_on(context)
|
|
208
|
+
|
|
209
|
+
tag_list = tag_list_cache_on(context).uniq
|
|
210
|
+
|
|
211
|
+
# Find existing tags or create non-existing tags:
|
|
212
|
+
tag_list = Tag.find_or_create_all_with_like_by_name(tag_list)
|
|
213
|
+
|
|
214
|
+
current_tags = tags_on(context)
|
|
215
|
+
old_tags = current_tags - tag_list
|
|
216
|
+
new_tags = tag_list - current_tags
|
|
217
|
+
|
|
218
|
+
# Find taggings to remove:
|
|
219
|
+
old_taggings = taggings.where(:tagger_type => nil, :tagger_id => nil,
|
|
220
|
+
:context => context.to_s, :tag_id => old_tags).all
|
|
221
|
+
|
|
222
|
+
if old_taggings.present?
|
|
223
|
+
# Destroy old taggings:
|
|
224
|
+
Tagging.destroy_all :id => old_taggings.map(&:id)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Create new taggings:
|
|
228
|
+
new_tags.each do |tag|
|
|
229
|
+
taggings.create!(:tag_id => tag.id, :context => context.to_s, :taggable => self)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
true
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module ActsAsTaggableOn::Taggable
|
|
2
|
+
module Ownership
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.send :include, ActsAsTaggableOn::Taggable::Ownership::InstanceMethods
|
|
5
|
+
base.extend ActsAsTaggableOn::Taggable::Ownership::ClassMethods
|
|
6
|
+
|
|
7
|
+
base.class_eval do
|
|
8
|
+
after_save :save_owned_tags
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
base.initialize_acts_as_taggable_on_ownership
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ClassMethods
|
|
15
|
+
def acts_as_taggable_on(*args)
|
|
16
|
+
initialize_acts_as_taggable_on_ownership
|
|
17
|
+
super(*args)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize_acts_as_taggable_on_ownership
|
|
21
|
+
tag_types.map(&:to_s).each do |tag_type|
|
|
22
|
+
class_eval %(
|
|
23
|
+
def #{tag_type}_from(owner)
|
|
24
|
+
owner_tag_list_on(owner, '#{tag_type}')
|
|
25
|
+
end
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module InstanceMethods
|
|
32
|
+
def owner_tags_on(owner, context)
|
|
33
|
+
base_tags.where([%(#{Tagging.table_name}.context = ? AND
|
|
34
|
+
#{Tagging.table_name}.tagger_id = ? AND
|
|
35
|
+
#{Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cached_owned_tag_list_on(context)
|
|
39
|
+
variable_name = "@owned_#{context}_list"
|
|
40
|
+
cache = instance_variable_get(variable_name) || instance_variable_set(variable_name, {})
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def owner_tag_list_on(owner, context)
|
|
44
|
+
add_custom_context(context)
|
|
45
|
+
|
|
46
|
+
cache = cached_owned_tag_list_on(context)
|
|
47
|
+
cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
|
|
48
|
+
|
|
49
|
+
cache[owner] ||= TagList.new(*owner_tags_on(owner, context).map(&:name))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def set_owner_tag_list_on(owner, context, new_list)
|
|
53
|
+
add_custom_context(context)
|
|
54
|
+
|
|
55
|
+
cache = cached_owned_tag_list_on(context)
|
|
56
|
+
cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
|
|
57
|
+
|
|
58
|
+
cache[owner] = TagList.from(new_list)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def reload
|
|
62
|
+
self.class.tag_types.each do |context|
|
|
63
|
+
instance_variable_set("@owned_#{context}_list", nil)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
super
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def save_owned_tags
|
|
70
|
+
tagging_contexts.each do |context|
|
|
71
|
+
cached_owned_tag_list_on(context).each do |owner, tag_list|
|
|
72
|
+
# Find existing tags or create non-existing tags:
|
|
73
|
+
tag_list = Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
|
|
74
|
+
|
|
75
|
+
owned_tags = owner_tags_on(owner, context)
|
|
76
|
+
old_tags = owned_tags - tag_list
|
|
77
|
+
new_tags = tag_list - owned_tags
|
|
78
|
+
|
|
79
|
+
# Find all taggings that belong to the taggable (self), are owned by the owner,
|
|
80
|
+
# have the correct context, and are removed from the list.
|
|
81
|
+
old_taggings = Tagging.where(:taggable_id => id, :taggable_type => self.class.base_class.to_s,
|
|
82
|
+
:tagger_type => owner.class.to_s, :tagger_id => owner.id,
|
|
83
|
+
:tag_id => old_tags, :context => context).all
|
|
84
|
+
|
|
85
|
+
if old_taggings.present?
|
|
86
|
+
# Destroy old taggings:
|
|
87
|
+
Tagging.destroy_all(:id => old_taggings.map(&:id))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Create new taggings:
|
|
91
|
+
new_tags.each do |tag|
|
|
92
|
+
taggings.create!(:tag_id => tag.id, :context => context.to_s, :tagger => owner, :taggable => self)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
true
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module ActsAsTaggableOn::Taggable
|
|
2
|
+
module Related
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.send :include, ActsAsTaggableOn::Taggable::Related::InstanceMethods
|
|
5
|
+
base.extend ActsAsTaggableOn::Taggable::Related::ClassMethods
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
def initialize_acts_as_taggable_on_related
|
|
10
|
+
tag_types.map(&:to_s).each do |tag_type|
|
|
11
|
+
class_eval %(
|
|
12
|
+
def find_related_#{tag_type}(options = {})
|
|
13
|
+
related_tags_for('#{tag_type}', self.class, options)
|
|
14
|
+
end
|
|
15
|
+
alias_method :find_related_on_#{tag_type}, :find_related_#{tag_type}
|
|
16
|
+
|
|
17
|
+
def find_related_#{tag_type}_for(klass, options = {})
|
|
18
|
+
related_tags_for('#{tag_type}', klass, options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def find_matching_contexts(search_context, result_context, options = {})
|
|
22
|
+
matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def find_matching_contexts_for(klass, search_context, result_context, options = {})
|
|
26
|
+
matching_contexts_for(search_context.to_s, result_context.to_s, klass, options)
|
|
27
|
+
end
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def acts_as_taggable_on(*args)
|
|
33
|
+
super(*args)
|
|
34
|
+
initialize_acts_as_taggable_on_related
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module InstanceMethods
|
|
39
|
+
def matching_contexts_for(search_context, result_context, klass, options = {})
|
|
40
|
+
tags_to_find = tags_on(search_context).collect { |t| t.name }
|
|
41
|
+
|
|
42
|
+
exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
|
|
43
|
+
|
|
44
|
+
klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
|
45
|
+
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
|
46
|
+
:conditions => ["#{exclude_self} #{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?) AND #{Tagging.table_name}.context = ?", tags_to_find, result_context],
|
|
47
|
+
:group => grouped_column_names_for(klass),
|
|
48
|
+
:order => "count DESC" }.update(options))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def related_tags_for(context, klass, options = {})
|
|
52
|
+
tags_to_find = tags_on(context).collect { |t| t.name }
|
|
53
|
+
|
|
54
|
+
exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
|
|
55
|
+
|
|
56
|
+
klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
|
57
|
+
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
|
58
|
+
:conditions => ["#{exclude_self} #{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?)", tags_to_find],
|
|
59
|
+
:group => grouped_column_names_for(klass),
|
|
60
|
+
:order => "count DESC" }.update(options))
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|