Module: ActiveRecord::Acts::TaggableOn::SingletonMethods

Defined in:
lib/acts_as_taggable_on/acts_as_taggable_on.rb

Instance Method Summary collapse

Instance Method Details

#all_tag_counts(options = {}) ⇒ Object



136
137
138
# File 'lib/acts_as_taggable_on/acts_as_taggable_on.rb', line 136

def all_tag_counts(options = {})
  Tag.find(:all, find_options_for_tag_counts(options))
end

#caching_tag_list_on?(context) ⇒ Boolean

Returns:

  • (Boolean)


128
129
130
# File 'lib/acts_as_taggable_on/acts_as_taggable_on.rb', line 128

def caching_tag_list_on?(context)
  column_names.include?("cached_#{context.to_s.singularize}_list")
end

#find_options_for_find_tagged_with(tags, options = {}) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/acts_as_taggable_on/acts_as_taggable_on.rb', line 140

def find_options_for_find_tagged_with(tags, options = {})
  tags = TagList.from(tags)
  
  return {} if tags.empty?
  
  joins = []
  conditions = []
  
  context = options.delete(:on)

  
  if options.delete(:exclude)
    tags_conditions = tags.map { |t| sanitize_sql(["#{Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
    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)})"
  
  else          
    tags.each do |tag|
      safe_tag = tag.gsub(/[^a-zA-Z0-9]/, '')
      prefix   = "#{safe_tag}_#{rand(1024)}"
  
      taggings_alias = "#{table_name}_taggings_#{prefix}"
      tags_alias     = "#{table_name}_tags_#{prefix}"
    
      tagging_join  = "JOIN #{Tagging.table_name} #{taggings_alias}" +
                      "  ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
                      " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
      tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
  
      tag_join     = "JOIN #{Tag.table_name} #{tags_alias}" +
                     "  ON #{tags_alias}.id = #{taggings_alias}.tag_id" +
                     " AND " + sanitize_sql(["#{tags_alias}.name like ?", tag])
  
      joins << tagging_join
      joins << tag_join
    end            
  end
  
  taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"

  if options.delete(:match_all)
    joins << "LEFT OUTER JOIN #{Tagging.table_name} #{taggings_alias}" +
             "  ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
             " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
     
    group = "#{table_name}.#{primary_key} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
  end
  
  { :joins      => joins.join(" "),
    :group      => group,
    :conditions => conditions.join(" AND ") }.update(options)
end

#find_options_for_tag_counts(options = {}) ⇒ Object

Calculate the tag counts for all tags.

Options:

:start_at - Restrict the tags to those created after a certain time
:end_at - Restrict the tags to those created before a certain time
:conditions - A piece of SQL conditions to add to the query
:limit - The maximum number of tags to return
:order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc'
:at_least - Exclude tags with a frequency less than the given value
:at_most - Exclude tags with a frequency greater than the given value
:on - Scope the find to only include a certain context


203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/acts_as_taggable_on/acts_as_taggable_on.rb', line 203

def find_options_for_tag_counts(options = {})
  options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id
  
  scope = scope(:find)
  start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
  end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]

  taggable_type = sanitize_sql(["#{Tagging.table_name}.taggable_type = ?", base_class.name])
  taggable_id = sanitize_sql(["#{Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
  options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
  
  conditions = [
    taggable_type,
    taggable_id,
    options[:conditions],
    start_at,
    end_at
  ]

  conditions = conditions.compact.join(' AND ')
  conditions = merge_conditions(conditions, scope[:conditions]) if scope

  joins = ["LEFT OUTER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
  joins << sanitize_sql(["AND #{Tagging.table_name}.context = ?",options.delete(:on).to_s]) unless options[:on].nil?
  
  joins << " INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
  unless self.descends_from_active_record?
    # Current model is STI descendant, so add type checking to the join condition
    joins << " AND #{table_name}.#{self.inheritance_column} = '#{self.name}'"
  end
  
  joins << scope[:joins] if scope && scope[:joins]

  at_least  = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
  at_most   = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
  having    = [at_least, at_most].compact.join(' AND ')
  group_by  = "#{Tag.table_name}.id HAVING COUNT(*) > 0"
  group_by << " AND #{having}" unless having.blank?

  { :select     => "#{Tag.table_name}.*, COUNT(*) AS count", 
    :joins      => joins.join(" "),
    :conditions => conditions,
    :group      => group_by,
    :limit      => options[:limit],
    :order      => options[:order]
  }
end

#find_tagged_with(*args) ⇒ Object

Pass either a tag string, or an array of strings or tags

Options:

:exclude - Find models that are not tagged with the given tags
:match_all - Find models that match all of the given tags, not just one
:conditions - A piece of SQL conditions to add to the query
:on - scopes the find to a context


123
124
125
126
# File 'lib/acts_as_taggable_on/acts_as_taggable_on.rb', line 123

def find_tagged_with(*args)
  options = find_options_for_find_tagged_with(*args)
  options.blank? ? [] : find(:all,options)
end

#is_taggable?Boolean

Returns:

  • (Boolean)


251
252
253
# File 'lib/acts_as_taggable_on/acts_as_taggable_on.rb', line 251

def is_taggable?
  true
end

#tag_counts_on(context, options = {}) ⇒ Object



132
133
134
# File 'lib/acts_as_taggable_on/acts_as_taggable_on.rb', line 132

def tag_counts_on(context, options = {})
  Tag.find(:all, find_options_for_tag_counts(options.merge({:on => context.to_s})))
end