acts-as-taggable-on 2.0.0.pre1 → 2.0.0.pre3
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/Gemfile +4 -6
- data/README.rdoc +10 -0
- data/Rakefile +44 -16
- data/VERSION +1 -1
- data/lib/acts-as-taggable-on.rb +19 -16
- data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +30 -415
- data/lib/acts_as_taggable_on/acts_as_taggable_on/aggregate.rb +90 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +39 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +202 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +74 -0
- data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +74 -0
- data/lib/acts_as_taggable_on/acts_as_tagger.rb +34 -44
- 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/compatibility/tag.rb +3 -0
- data/lib/acts_as_taggable_on/compatibility/tagging.rb +3 -0
- data/lib/acts_as_taggable_on/tag.rb +16 -5
- data/lib/acts_as_taggable_on/tags_helper.rb +1 -1
- data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +37 -29
- data/spec/acts_as_taggable_on/tag_spec.rb +13 -2
- data/spec/acts_as_taggable_on/taggable_spec.rb +35 -21
- data/spec/acts_as_taggable_on/tagger_spec.rb +17 -1
- data/spec/acts_as_taggable_on/tagging_spec.rb +6 -1
- data/spec/acts_as_taggable_on/tags_helper_spec.rb +0 -2
- data/spec/models.rb +32 -0
- data/spec/schema.rb +3 -1
- data/spec/spec_helper.rb +18 -37
- metadata +14 -6
- data/lib/acts_as_taggable_on/group_helper.rb +0 -14
- data/spec/acts_as_taggable_on/group_helper_spec.rb +0 -21
@@ -0,0 +1,90 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable
|
2
|
+
module Aggregate
|
3
|
+
def self.included(base)
|
4
|
+
include InstanceMethods
|
5
|
+
base.extend ClassMethods
|
6
|
+
|
7
|
+
base.tag_types.map(&:to_s).each do |tag_type|
|
8
|
+
base.class_eval %(
|
9
|
+
def #{tag_type.singularize}_counts(options = {})
|
10
|
+
tag_counts_on('#{tag_type}', options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def top_#{tag_type}(limit = 10)
|
14
|
+
tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.top_#{tag_type}(limit = 10)
|
18
|
+
tag_counts_on('#{tag_type}', :order => 'count desc', :limit => limit.to_i)
|
19
|
+
end
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def tag_counts_on(context, options = {})
|
26
|
+
find_for_tag_counts(options.merge({:on => context.to_s}))
|
27
|
+
end
|
28
|
+
|
29
|
+
def all_tag_counts(options = {})
|
30
|
+
find_for_tag_counts(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Calculate the tag counts for all tags.
|
34
|
+
#
|
35
|
+
# Options:
|
36
|
+
# :start_at - Restrict the tags to those created after a certain time
|
37
|
+
# :end_at - Restrict the tags to those created before a certain time
|
38
|
+
# :conditions - A piece of SQL conditions to add to the query
|
39
|
+
# :limit - The maximum number of tags to return
|
40
|
+
# :order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc'
|
41
|
+
# :at_least - Exclude tags with a frequency less than the given value
|
42
|
+
# :at_most - Exclude tags with a frequency greater than the given value
|
43
|
+
# :on - Scope the find to only include a certain context
|
44
|
+
def find_for_tag_counts(options = {})
|
45
|
+
options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
|
46
|
+
|
47
|
+
start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
|
48
|
+
end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
|
49
|
+
|
50
|
+
taggable_type = sanitize_sql(["#{Tagging.table_name}.taggable_type = ?", base_class.name])
|
51
|
+
taggable_id = sanitize_sql(["#{Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
|
52
|
+
options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
|
53
|
+
|
54
|
+
conditions = [
|
55
|
+
taggable_type,
|
56
|
+
taggable_id,
|
57
|
+
options[:conditions],
|
58
|
+
start_at,
|
59
|
+
end_at
|
60
|
+
]
|
61
|
+
|
62
|
+
conditions = conditions.compact.join(' AND ')
|
63
|
+
|
64
|
+
joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
|
65
|
+
joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
|
66
|
+
joins << " INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
|
67
|
+
|
68
|
+
unless descends_from_active_record?
|
69
|
+
# Current model is STI descendant, so add type checking to the join condition
|
70
|
+
joins << " AND #{table_name}.#{inheritance_column} = '#{name}'"
|
71
|
+
end
|
72
|
+
|
73
|
+
at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
|
74
|
+
at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
|
75
|
+
having = [at_least, at_most].compact.join(' AND ')
|
76
|
+
group_by = "#{grouped_column_names_for(Tag)} HAVING COUNT(*) > 0"
|
77
|
+
group_by << " AND #{having}" unless having.blank?
|
78
|
+
|
79
|
+
Tag.select("#{Tag.table_name}.*, COUNT(*) AS count").joins(joins.join(" ")).where(conditions).group(group_by).limit(options[:limit]).order(options[:order])
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
module InstanceMethods
|
85
|
+
def tag_counts_on(context, options={})
|
86
|
+
self.class.tag_counts_on(context, options.merge(:id => id))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable
|
2
|
+
module Cache
|
3
|
+
def self.included(base)
|
4
|
+
# Skip adding caching capabilities if no cache columns exist
|
5
|
+
return unless base.tag_types.any? { |context| base.column_names.include?("cached_#{context.to_s.singularize}_list") }
|
6
|
+
|
7
|
+
base.class_eval do
|
8
|
+
before_save :save_cached_tag_list
|
9
|
+
end
|
10
|
+
|
11
|
+
base.tag_types.map(&:to_s).each do |tag_type|
|
12
|
+
base.class_eval %(
|
13
|
+
def self.caching_#{tag_type.singularize}_list?
|
14
|
+
caching_tag_list_on?("#{tag_type}")
|
15
|
+
end
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
base.extend ClassMethods
|
20
|
+
include InstanceMethods
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def caching_tag_list_on?(context)
|
25
|
+
column_names.include?("cached_#{context.to_s.singularize}_list")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceMethods
|
30
|
+
def save_cached_tag_list
|
31
|
+
tag_types.map(&:to_s).each do |tag_type|
|
32
|
+
if self.class.send("caching_#{tag_type.singularize}_list?")
|
33
|
+
self["cached_#{tag_type.singularize}_list"] = tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', ')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable
|
2
|
+
module Core
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
attr_writer :custom_contexts
|
6
|
+
|
7
|
+
after_save :save_tags
|
8
|
+
|
9
|
+
def self.tagged_with(*args)
|
10
|
+
find_options_for_find_tagged_with(*args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
base.tag_types.map(&:to_s).each do |tag_type|
|
15
|
+
context_taggings = "#{tag_type.singularize}_taggings".to_sym
|
16
|
+
context_tags = tag_type.to_sym
|
17
|
+
|
18
|
+
base.class_eval do
|
19
|
+
has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag,
|
20
|
+
:conditions => ['#{Tagging.table_name}.context = ?', tag_type], :class_name => "Tagging"
|
21
|
+
has_many context_tags, :through => context_taggings, :source => :tag
|
22
|
+
end
|
23
|
+
|
24
|
+
base.class_eval %(
|
25
|
+
def self.#{tag_type.singularize}_counts(options={})
|
26
|
+
tag_counts_on('#{tag_type}', options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def #{tag_type.singularize}_list
|
30
|
+
tag_list_on('#{tag_type}')
|
31
|
+
end
|
32
|
+
|
33
|
+
def #{tag_type.singularize}_list=(new_tags)
|
34
|
+
set_tag_list_on('#{tag_type}', new_tags)
|
35
|
+
end
|
36
|
+
|
37
|
+
def all_#{tag_type}_list
|
38
|
+
all_tags_list_on('#{tag_type}')
|
39
|
+
end
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
base.extend ClassMethods
|
44
|
+
include InstanceMethods
|
45
|
+
end
|
46
|
+
|
47
|
+
module ClassMethods
|
48
|
+
# all column names are necessary for PostgreSQL group clause
|
49
|
+
def grouped_column_names_for(object)
|
50
|
+
object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_options_for_find_tagged_with(tags, options = {})
|
54
|
+
tag_list = TagList.from(tags)
|
55
|
+
|
56
|
+
return {} if tag_list.empty?
|
57
|
+
|
58
|
+
joins = []
|
59
|
+
conditions = []
|
60
|
+
|
61
|
+
context = options.delete(:on)
|
62
|
+
|
63
|
+
if options.delete(:exclude)
|
64
|
+
tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
|
65
|
+
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)})"
|
66
|
+
|
67
|
+
elsif options.delete(:any)
|
68
|
+
tags_conditions = tag_list.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
|
69
|
+
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)})"
|
70
|
+
|
71
|
+
else
|
72
|
+
tags = Tag.named_any(tag_list)
|
73
|
+
return where("1 = 0") unless tags.length == tag_list.length
|
74
|
+
|
75
|
+
tags.each do |tag|
|
76
|
+
safe_tag = tag.name.gsub(/[^a-zA-Z0-9]/, '')
|
77
|
+
prefix = "#{safe_tag}_#{rand(1024)}"
|
78
|
+
|
79
|
+
taggings_alias = "#{table_name}_taggings_#{prefix}"
|
80
|
+
|
81
|
+
tagging_join = "JOIN #{Tagging.table_name} #{taggings_alias}" +
|
82
|
+
" ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
|
83
|
+
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
|
84
|
+
" AND #{taggings_alias}.tag_id = #{tag.id}"
|
85
|
+
tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
|
86
|
+
|
87
|
+
joins << tagging_join
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"
|
92
|
+
|
93
|
+
if options.delete(:match_all)
|
94
|
+
joins << "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias}" +
|
95
|
+
" ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
|
96
|
+
" AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
|
97
|
+
|
98
|
+
group = "#{grouped_column_names_for(self)} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
|
99
|
+
end
|
100
|
+
|
101
|
+
joins(joins.join(" ")).group(group).where(conditions.join(" AND ")).readonly(false)
|
102
|
+
end
|
103
|
+
|
104
|
+
def is_taggable?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
module InstanceMethods
|
111
|
+
# all column names are necessary for PostgreSQL group clause
|
112
|
+
def grouped_column_names_for(object)
|
113
|
+
object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
|
114
|
+
end
|
115
|
+
|
116
|
+
def custom_contexts
|
117
|
+
@custom_contexts ||= []
|
118
|
+
end
|
119
|
+
|
120
|
+
def is_taggable?
|
121
|
+
self.class.is_taggable?
|
122
|
+
end
|
123
|
+
|
124
|
+
def add_custom_context(value)
|
125
|
+
custom_contexts << value.to_s unless custom_contexts.include?(value.to_s) or self.class.tag_types.map(&:to_s).include?(value.to_s)
|
126
|
+
end
|
127
|
+
|
128
|
+
def cached_tag_list_on(context)
|
129
|
+
self["cached_#{context.to_s.singularize}_list"]
|
130
|
+
end
|
131
|
+
|
132
|
+
def tag_list_cache_on(context)
|
133
|
+
variable_name = "@#{context.to_s.singularize}_list"
|
134
|
+
instance_variable_get(variable_name) || instance_variable_set(variable_name, TagList.new(tags_on(context).map(&:name)))
|
135
|
+
end
|
136
|
+
|
137
|
+
def tag_list_on(context)
|
138
|
+
add_custom_context(context)
|
139
|
+
tag_list_cache_on(context)
|
140
|
+
end
|
141
|
+
|
142
|
+
def all_tags_list_on(context)
|
143
|
+
variable_name = "@all_#{context.to_s.singularize}_list"
|
144
|
+
return instance_variable_get(variable_name) if instance_variable_get(variable_name)
|
145
|
+
instance_variable_set(variable_name, TagList.new(all_tags_on(context).map(&:name)).freeze)
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Returns all tags of a given context
|
150
|
+
def all_tags_on(context)
|
151
|
+
opts = ["#{Tagging.table_name}.context = ?", context.to_s]
|
152
|
+
base_tags.where(opts).order("#{Tagging.table_name}.created_at")
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Returns all tags that are not owned of a given context
|
157
|
+
def tags_on(context)
|
158
|
+
base_tags.where(["#{Tagging.table_name}.context = ? AND #{Tagging.table_name}.tagger_id IS NULL", context.to_s]).all
|
159
|
+
end
|
160
|
+
|
161
|
+
def set_tag_list_on(context, new_list)
|
162
|
+
add_custom_context(context)
|
163
|
+
|
164
|
+
variable_name = "@#{context.to_s.singularize}_list"
|
165
|
+
instance_variable_set(variable_name, TagList.from(new_list))
|
166
|
+
end
|
167
|
+
|
168
|
+
def tagging_contexts
|
169
|
+
custom_contexts + self.class.tag_types.map(&:to_s)
|
170
|
+
end
|
171
|
+
|
172
|
+
def save_tags
|
173
|
+
transaction do
|
174
|
+
tagging_contexts.each do |context|
|
175
|
+
tag_list = tag_list_cache_on(context).uniq
|
176
|
+
|
177
|
+
# Find existing tags or create non-existing tags:
|
178
|
+
tag_list = Tag.find_or_create_all_with_like_by_name(tag_list)
|
179
|
+
|
180
|
+
current_tags = tags_on(context)
|
181
|
+
old_tags = current_tags - tag_list
|
182
|
+
new_tags = tag_list - current_tags
|
183
|
+
|
184
|
+
# Find taggings to remove:
|
185
|
+
old_taggings = Tagging.where(:taggable_id => self.id, :taggable_type => self.class.base_class.to_s,
|
186
|
+
:tagger_type => nil, :tagger_id => nil,
|
187
|
+
:context => context, :tag_id => old_tags)
|
188
|
+
|
189
|
+
Tagging.destroy_all :id => old_taggings.map(&:id)
|
190
|
+
|
191
|
+
# Create new taggings:
|
192
|
+
new_tags.each do |tag|
|
193
|
+
Tagging.create!(:tag_id => tag.id, :context => context, :taggable => self)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
true
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable
|
2
|
+
module Ownership
|
3
|
+
def self.included(base)
|
4
|
+
include InstanceMethods
|
5
|
+
base.extend ClassMethods
|
6
|
+
|
7
|
+
base.tag_types.map(&:to_s).each do |tag_type|
|
8
|
+
base.class_eval %(
|
9
|
+
def #{tag_type}_from(owner)
|
10
|
+
owner_tag_list_on(owner, '#{tag_type}')
|
11
|
+
end
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
end
|
18
|
+
|
19
|
+
module InstanceMethods
|
20
|
+
def cached_owned_tag_list_on(context)
|
21
|
+
variable_name = "@owned_#{context}_list"
|
22
|
+
cache = instance_variable_get(variable_name) || instance_variable_set(variable_name, {})
|
23
|
+
end
|
24
|
+
|
25
|
+
def owner_tag_list_on(owner, context)
|
26
|
+
cache = cached_owned_tag_list_on(context)
|
27
|
+
cache[owner] ||= TagList.new(*owner_tags_on(owner, context).map(&:name))
|
28
|
+
end
|
29
|
+
|
30
|
+
def owner_tags_on(owner, context)
|
31
|
+
base_tags.where([%(#{Tagging.table_name}.context = ? AND
|
32
|
+
#{Tagging.table_name}.tagger_id = ? AND
|
33
|
+
#{Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s])
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_owner_tag_list_on(owner, context, new_list)
|
37
|
+
cache = cached_owned_tag_list_on(context)
|
38
|
+
cache[owner] = TagList.from(new_list)
|
39
|
+
end
|
40
|
+
|
41
|
+
def save_tags
|
42
|
+
transaction do
|
43
|
+
tagging_contexts.each do |context|
|
44
|
+
cached_owned_tag_list_on(context).each do |owner, tag_list|
|
45
|
+
# Find existing tags or create non-existing tags:
|
46
|
+
tag_list = Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
|
47
|
+
|
48
|
+
owned_tags = owner_tags_on(owner, context)
|
49
|
+
|
50
|
+
old_tags = owned_tags - tag_list
|
51
|
+
new_tags = tag_list - owned_tags
|
52
|
+
|
53
|
+
# Find all taggings that belong to the taggable (self), are owned by the owner,
|
54
|
+
# have the correct context, and are removed from the list.
|
55
|
+
old_taggings = Tagging.where(:taggable_id => id, :taggable_type => self.class.base_class.to_s,
|
56
|
+
:tagger_type => owner.class.to_s, :tagger_id => owner.id,
|
57
|
+
:tag_id => old_tags, :context => context)
|
58
|
+
|
59
|
+
# Destroy old taggings:
|
60
|
+
Tagging.destroy_all(:id => old_taggings.map(&:id))
|
61
|
+
|
62
|
+
# Create new taggings:
|
63
|
+
new_tags.each do |tag|
|
64
|
+
Tagging.create!(:tag_id => tag.id, :context => context, :tagger => owner, :taggable => self)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ActsAsTaggableOn::Taggable
|
2
|
+
module Related
|
3
|
+
def self.included(base)
|
4
|
+
include InstanceMethods
|
5
|
+
base.extend ClassMethods
|
6
|
+
|
7
|
+
base.tag_types.map(&:to_s).each do |tag_type|
|
8
|
+
base.class_eval %(
|
9
|
+
def find_related_#{tag_type}(options = {})
|
10
|
+
related_tags_for('#{tag_type}', self.class, options)
|
11
|
+
end
|
12
|
+
alias_method :find_related_on_#{tag_type}, :find_related_#{tag_type}
|
13
|
+
|
14
|
+
def find_related_#{tag_type}_for(klass, options = {})
|
15
|
+
related_tags_for('#{tag_type}', klass, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_matching_contexts(search_context, result_context, options = {})
|
19
|
+
matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_matching_contexts_for(klass, search_context, result_context, options = {})
|
23
|
+
matching_contexts_for(search_context.to_s, result_context.to_s, klass, options)
|
24
|
+
end
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
end
|
31
|
+
|
32
|
+
module InstanceMethods
|
33
|
+
def matching_contexts_for(search_context, result_context, klass, options = {})
|
34
|
+
search_conditions = matching_context_search_options(search_context, result_context, klass, options)
|
35
|
+
|
36
|
+
# klass.select(search_conditions[:select]).from(search_conditions[:from]).where(search_conditions[:conditions]).group(search_conditions[:group]).order(search_conditions[:order])
|
37
|
+
klass.scoped(search_conditions)
|
38
|
+
end
|
39
|
+
|
40
|
+
def matching_context_search_options(search_context, result_context, klass, options = {})
|
41
|
+
tags_to_find = tags_on(search_context).collect { |t| t.name }
|
42
|
+
|
43
|
+
exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
|
44
|
+
|
45
|
+
{ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
46
|
+
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
47
|
+
: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],
|
48
|
+
:group => grouped_column_names_for(klass),
|
49
|
+
:order => "count DESC"
|
50
|
+
}.update(options)
|
51
|
+
end
|
52
|
+
|
53
|
+
def related_tags_for(context, klass, options = {})
|
54
|
+
search_conditions = related_search_options(context, klass, options)
|
55
|
+
|
56
|
+
# klass.select(search_conditions[:select]).from(search_conditions[:from]).where(search_conditions[:conditions]).group(search_conditions[:group]).order(search_conditions[:order])
|
57
|
+
klass.scoped(search_conditions)
|
58
|
+
end
|
59
|
+
|
60
|
+
def related_search_options(context, klass, options = {})
|
61
|
+
tags_to_find = tags_on(context).collect { |t| t.name }
|
62
|
+
|
63
|
+
exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
|
64
|
+
|
65
|
+
{ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
|
66
|
+
:from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
|
67
|
+
: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],
|
68
|
+
:group => grouped_column_names_for(klass),
|
69
|
+
:order => "count DESC"
|
70
|
+
}.update(options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|