module ActsAsTaggableOn::Taggable::Collection

Public Instance Methods

acts_as_taggable_on(*args) click to toggle source
Calls superclass method
# File lib/acts-as-taggable-on/taggable/collection.rb, line 35
def acts_as_taggable_on(*args)
  super(*args)
  initialize_acts_as_taggable_on_collection
end
all_tag_counts(options = {}) click to toggle source

Calculate the tag counts for all tags.

@param [Hash] options 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
# File lib/acts-as-taggable-on/taggable/collection.rb, line 94
def all_tag_counts(options = {})
  options = options.dup
  options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :on, :id

  ## Generate conditions:
  options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]

  ## Generate scope:
  tagging_scope = ActsAsTaggableOn::Tagging.select("#{ActsAsTaggableOn::Tagging.table_name}.tag_id, COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) AS tags_count")
  tag_scope = ActsAsTaggableOn::Tag.select("#{ActsAsTaggableOn::Tag.table_name}.*, #{ActsAsTaggableOn::Tagging.table_name}.tags_count AS count").order(options[:order]).limit(options[:limit])

  # Current model is STI descendant, so add type checking to the join condition
  unless descends_from_active_record?
    taggable_join = "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id"
    taggable_join =  taggable_join + " AND #{table_name}.#{inheritance_column} = '#{name}'"
    tagging_scope = tagging_scope.joins(taggable_join)
  end

  # Conditions
  tagging_conditions(options).each { |condition| tagging_scope = tagging_scope.where(condition) }
  tag_scope = tag_scope.where(options[:conditions])

  # GROUP BY and HAVING clauses:
  having = ["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) > 0"]
  if options[:at_least]
    having.push sanitize_sql(["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) >= ?",
                              options.delete(:at_least)])
  end
  if options[:at_most]
    having.push sanitize_sql(["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) <= ?",
                              options.delete(:at_most)])
  end
  having = having.compact.join(' AND ')

  group_columns = "#{ActsAsTaggableOn::Tagging.table_name}.tag_id"

  unless options[:id]
    # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
    tagging_scope = generate_tagging_scope_in_clause(tagging_scope, table_name, primary_key)
  end

  tagging_scope = tagging_scope.group(group_columns).having(having)

  tag_scope_joins(tag_scope, tagging_scope)
end
all_tags(options = {}) click to toggle source

Calculate the tag names. To be used when you don’t need tag counts and want to avoid the taggable joins.

@param [Hash] options 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. Note we don't join the taggable objects for performance reasons.
* :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'
* :on         - Scope the find to only include a certain context
# File lib/acts-as-taggable-on/taggable/collection.rb, line 59
def all_tags(options = {})
  options = options.dup
  options.assert_valid_keys :start_at, :end_at, :conditions, :order, :limit, :on

  ## Generate conditions:
  options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]

  ## Generate scope:
  tagging_scope = ActsAsTaggableOn::Tagging.select("#{ActsAsTaggableOn::Tagging.table_name}.tag_id")
  tag_scope = ActsAsTaggableOn::Tag.select("#{ActsAsTaggableOn::Tag.table_name}.*").order(options[:order]).limit(options[:limit])

  # Joins and conditions
  tagging_conditions(options).each { |condition| tagging_scope = tagging_scope.where(condition) }
  tag_scope = tag_scope.where(options[:conditions])

  group_columns = "#{ActsAsTaggableOn::Tagging.table_name}.tag_id"

  # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
  tagging_scope = generate_tagging_scope_in_clause(tagging_scope, table_name, primary_key).group(group_columns)

  tag_scope_joins(tag_scope, tagging_scope)
end
generate_tagging_scope_in_clause(tagging_scope, table_name, primary_key) click to toggle source
# File lib/acts-as-taggable-on/taggable/collection.rb, line 152
def generate_tagging_scope_in_clause(tagging_scope, table_name, primary_key)
  table_name_pkey = "#{table_name}.#{primary_key}"
  if ActsAsTaggableOn::Utils.using_mysql?
    # See https://github.com/mbleigh/acts-as-taggable-on/pull/457 for details
    scoped_ids = pluck(table_name_pkey)
    tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN (?)",
                                        scoped_ids)
  else
    tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{safe_to_sql(except(:select).select(table_name_pkey))})")
  end

  tagging_scope
end
initialize_acts_as_taggable_on_collection() click to toggle source
# File lib/acts-as-taggable-on/taggable/collection.rb, line 13
        def initialize_acts_as_taggable_on_collection
          tag_types.map(&:to_s).each do |tag_type|
            class_eval <<-RUBY, __FILE__, __LINE__ + 1
            def self.#{tag_type.singularize}_counts(options={})
              tag_counts_on('#{tag_type}', options)
            end

            def #{tag_type.singularize}_counts(options = {})
              tag_counts_on('#{tag_type}', options)
            end

            def top_#{tag_type}(limit = 10)
              tag_counts_on('#{tag_type}', order: 'count desc', limit: limit.to_i)
            end

            def self.top_#{tag_type}(limit = 10)
              tag_counts_on('#{tag_type}', order: 'count desc', limit: limit.to_i)
            end
            RUBY
          end
        end
safe_to_sql(relation) click to toggle source
# File lib/acts-as-taggable-on/taggable/collection.rb, line 140
def safe_to_sql(relation)
  if connection.respond_to?(:unprepared_statement)
    connection.unprepared_statement do
      relation.to_sql
    end
  else
    relation.to_sql
  end
end
tag_counts_on(context, options = {}) click to toggle source
# File lib/acts-as-taggable-on/taggable/collection.rb, line 40
def tag_counts_on(context, options = {})
  all_tag_counts(options.merge({ on: context.to_s }))
end
tag_scope_joins(tag_scope, tagging_scope) click to toggle source
# File lib/acts-as-taggable-on/taggable/collection.rb, line 199
def tag_scope_joins(tag_scope, tagging_scope)
  tag_scope = tag_scope.joins("JOIN (#{safe_to_sql(tagging_scope)}) AS #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id")
  tag_scope.extending(CalculationMethods)
end
tagging_conditions(options) click to toggle source
# File lib/acts-as-taggable-on/taggable/collection.rb, line 166
def tagging_conditions(options)
  tagging_conditions = []
  if options[:end_at]
    tagging_conditions.push sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?",
                                          options.delete(:end_at)])
  end
  if options[:start_at]
    tagging_conditions.push sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?",
                                          options.delete(:start_at)])
  end

  taggable_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.taggable_type = ?",
                                      base_class.name])
  if options[:on]
    taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?",
                                         options.delete(:on).to_s])
  end

  if options[:id]
    taggable_conditions << if options[:id].is_a? Array
                             sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN (?)",
                                           options[:id]])
                           else
                             sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = ?",
                                           options[:id]])
                           end
  end

  tagging_conditions.push taggable_conditions

  tagging_conditions
end
tags_on(context, options = {}) click to toggle source
# File lib/acts-as-taggable-on/taggable/collection.rb, line 44
def tags_on(context, options = {})
  all_tags(options.merge({ on: context.to_s }))
end