module ActsAsTaggableOn::Taggable::Core::ClassMethods

Public Instance Methods

adjust_taggings_alias(taggings_alias) click to toggle source
# File lib/acts_as_taggable_on/acts_as_taggable_on/core.rb, line 186
def adjust_taggings_alias(taggings_alias)
  if taggings_alias.size > 75
    taggings_alias = 'taggings_alias_' + Digest::SHA1.hexdigest(taggings_alias)
  end
  taggings_alias
end
grouped_column_names_for(object) click to toggle source

all column names are necessary for PostgreSQL group clause

# File lib/acts_as_taggable_on/acts_as_taggable_on/core.rb, line 61
def grouped_column_names_for(object)
  object.column_names.map { |column| "#{object.table_name}.#{column}" }.join(", ")
end
initialize_acts_as_taggable_on_core() click to toggle source
# File lib/acts_as_taggable_on/acts_as_taggable_on/core.rb, line 16
def initialize_acts_as_taggable_on_core
  tag_types.map(&:to_s).each do |tags_type|
    tag_type         = tags_type.to_s.singularize
    context_taggings = "#{tag_type}_taggings".to_sym
    context_tags     = tags_type.to_sym
    taggings_order   = (preserve_tag_order? ? "#{ActsAsTaggableOn::Tagging.table_name}.id" : nil)
    
    class_eval do
      # when preserving tag order, include order option so that for a 'tags' context
      # the associations tag_taggings & tags are always returned in created order
      has_many context_taggings, :as => :taggable,
                                 :dependent => :destroy,
                                 :include => :tag,
                                 :class_name => "ActsAsTaggableOn::Tagging",
                                 :conditions => ["#{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_type],
                                 :order => taggings_order
                                 
      has_many context_tags, :through => context_taggings,
                             :source => :tag,
                             :class_name => "ActsAsTaggableOn::Tag",
                             :order => taggings_order
    end

    class_eval %Q(
      def #{tag_type}_list
        tag_list_on('#{tags_type}')
      end

      def #{tag_type}_list=(new_tags)
        set_tag_list_on('#{tags_type}', new_tags)
      end

      def all_#{tags_type}_list
        all_tags_list_on('#{tags_type}')
      end
    )
  end
end
is_taggable?() click to toggle source
# File lib/acts_as_taggable_on/acts_as_taggable_on/core.rb, line 182
def is_taggable?
  true
end
taggable_on(preserve_tag_order, *tag_types) click to toggle source
# File lib/acts_as_taggable_on/acts_as_taggable_on/core.rb, line 55
def taggable_on(preserve_tag_order, *tag_types)
  super(preserve_tag_order, *tag_types)
  initialize_acts_as_taggable_on_core
end
tagged_with(tags, options = {}) click to toggle source

Return a scope of objects that are tagged with the specified tags.

@param tags The tags that we want to query for @param [Hash] options A hash of options to alter you query:

* <tt>:exclude</tt> - if set to true, return objects that are *NOT* tagged with the specified tags
* <tt>:any</tt> - if set to true, return objects that are tagged with *ANY* of the specified tags
* <tt>:match_all</tt> - if set to true, return objects that are *ONLY* tagged with the specified tags
* <tt>:owned_by</tt> - return objects that are *ONLY* owned by the owner

Example:

User.tagged_with("awesome", "cool")                     # Users that are tagged with awesome and cool
User.tagged_with("awesome", "cool", :exclude => true)   # Users that are not tagged with awesome or cool
User.tagged_with("awesome", "cool", :any => true)       # Users that are tagged with awesome or cool
User.tagged_with("awesome", "cool", :match_all => true) # Users that are tagged with just awesome and cool
User.tagged_with("awesome", "cool", :owned_by => foo ) # Users that are tagged with just awesome and cool by 'foo'
# File lib/acts_as_taggable_on/acts_as_taggable_on/core.rb, line 81
def tagged_with(tags, options = {})
  tag_list = ActsAsTaggableOn::TagList.from(tags)
  empty_result = scoped(:conditions => "1 = 0")

  return empty_result if tag_list.empty?

  joins = []
  conditions = []

  context = options.delete(:on)
  owned_by = options.delete(:owned_by)
  alias_base_name = undecorated_table_name.gsub('.','_')
  quote = ActsAsTaggableOn::Tag.using_postgresql? ? '"' : ''

  if options.delete(:exclude)
    if options.delete(:wild)
      tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ? ESCAPE '!'", "%#{escape_like(t)}%"]) }.join(" OR ")
    else
      tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ?", t]) }.join(" OR ")
    end

    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)})"

  elsif options.delete(:any)
    # get tags, drop out if nothing returned (we need at least one)
    if options.delete(:wild)
      tags = ActsAsTaggableOn::Tag.named_like_any(tag_list)
    else
      tags = ActsAsTaggableOn::Tag.named_any(tag_list)
    end

    return scoped(:conditions => "1 = 0") unless tags.length > 0

    # setup taggings alias so we can chain, ex: items_locations_taggings_awesome_cool_123
    # avoid ambiguous column name
    taggings_context = context ? "_#{context}" : ''

    taggings_alias   = adjust_taggings_alias(
      "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{sha_prefix(tags.map(&:name).join('_'))}"
    )

    tagging_join  = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
                    "  ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{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

    # don't need to sanitize sql, map all ids and join with OR logic
    conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{t.id}" }.join(" OR ")
    select_clause = "DISTINCT #{table_name}.*" unless context and tag_types.one?

    joins << tagging_join

  else
    tags = ActsAsTaggableOn::Tag.named_any(tag_list)
    return empty_result unless tags.length == tag_list.length

    tags.each do |tag|

      taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{sha_prefix(tag.name)}")

      tagging_join  = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
                      "  ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
                      " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}" +
                      " AND #{taggings_alias}.tag_id = #{tag.id}"
      tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context

      if owned_by
          tagging_join << " AND " +
              sanitize_sql([
                  "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
                  owned_by.id,
                  owned_by.class.base_class.to_s
              ])
      end

      joins << tagging_join
    end
  end

  taggings_alias, tags_alias = adjust_taggings_alias("#{alias_base_name}_taggings_group"), "#{alias_base_name}_tags_group"

  if options.delete(:match_all)
    joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
             "  ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
             " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"


    group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
    group = group_columns
    having = "COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
  end

  scoped(:select     => select_clause,
         :joins      => joins.join(" "),
         :group      => group,
         :having     => having,
         :conditions => conditions.join(" AND "),
         :order      => options[:order],
         :readonly   => false)
end