Top Level Namespace

Defined Under Namespace

Modules: RadiantTagsExtension, RadiusTags Classes: MetaTag, TagSearchPage, Tagging

Constant Summary collapse

TaggingMethods =
Proc.new do
  
  def valid_with_tags?
    return (self.errors.empty? && valid_without_tags?)
  end
  alias_method_chain :valid?, :tags
  
  def save_with_tags!
    self.save_without_tags!
    # just skip the whole method if the tags string hasn't changed
    return self if @new_tags == tag_list
    
    # tags have changed, so we delete all taggings and re-create to preserve order
    taggings.clear
    
    @new_tags.to_s.split(MetaTag::DELIMITER).each do |tag|
      begin
        tag = MetaTag.find_or_initialize_by_name(tag.strip.squeeze(" "))
        meta_tags << tag unless meta_tags.include?(tag)
      rescue ActiveRecord::RecordInvalid => e
        errors.add_to_base("Tags can not contain special characters")
      rescue ActiveRecord::StatementInvalid => e
        # With SQLite3 - a duplicate tagging will result in the following message:
        # SQLite3::SQLException: SQL logic error or missing database: INSERT INTO taggings ("meta_tag_id", "taggable_type", "taggable_id") VALUES(11, 'Page', 74)
        # raise unless e.to_s =~ /duplicate/i
      end
    end
  end
  alias_method_chain :save!, :tags
  
  def meta_tags=(tags)
    @new_tags = tags
  end

  def ordered_meta_tags
    # HACK: need to order by tagging to preserve creation order, otherwise
    # list gets ordered by tag.id
    meta_tags.find(:all, :order => 'taggings.id')
  end

  def tag_list
    ordered_meta_tags.map(&:name).join(MetaTag::DELIMITER)
  end

   # 
   # Find all the objects tagged with the supplied list of tags
   # 
   # Usage : Model.tagged_with("ruby")
   #         Model.tagged_with("hello", "world")
   #         Model.tagged_with("hello", "world", :limit => 10)
   #
   def self.tagged_with(*tag_list)
     options = tag_list.extract_options!
     tag_list = parse_tags(tag_list)
   
     scope = scope(:find)
     options[:select] ||= "#{table_name}.*"
     options[:from] ||= "#{table_name}, meta_tags, taggings"
   
     sql  = "SELECT #{(scope && scope[:select]) || options[:select]} "
     sql << "FROM #{(scope && scope[:from]) || options[:from]} "

     add_joins!(sql, options[:joins], scope)
   
     sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
     sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
     sql << "AND taggings.meta_tag_id = meta_tags.id "
     sql << "AND pages.site_id = #{current_site.id} " if self.respond_to?(:current_site)
     
     sql << "AND (#{sanitize_sql(['meta_tags.name IN (?)', tag_list.map{|name| name.strip.squeeze(' ')}])}) "
     sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
      
     columns = column_names.map do |column| 
       "#{table_name}.#{column}"
     end.join(", ")
      
     sql << "GROUP BY #{columns} "
     sql << "HAVING COUNT(taggings.meta_tag_id) = #{tag_list.size}"
   
     add_order!(sql, options[:order], scope)
     add_limit!(sql, options, scope)
     add_lock!(sql, options, scope)

   
     find_by_sql(sql)
   end
   
   def self.tagged_with_any(*tag_list)
     options = tag_list.extract_options!
     tag_list = parse_tags(tag_list)
   
     scope = scope(:find)
     options[:select] ||= "#{table_name}.*"
     options[:from] ||= "#{table_name}, meta_tags, taggings"
   
     sql  = "SELECT #{(scope && scope[:select]) || options[:select]} "
     sql << "FROM #{(scope && scope[:from]) || options[:from]} "

     add_joins!(sql, options[:joins], scope)
   
     sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
     sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
     sql << "AND taggings.meta_tag_id = meta_tags.id "
     sql << "AND pages.site_id = #{current_site.id} " if self.respond_to?(:current_site)
     
     sql << "AND ("
     sql << tag_list.inject([]) do |arr,name|
              arr << sanitize_sql(["meta_tags.name = ?", name.strip.squeeze(' ')])
            end.join(" OR ")
     sql << ") "
     
     
     sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
      
     columns = column_names.map do |column| 
       "#{table_name}.#{column}"
     end.join(", ")
      
     sql << "GROUP BY #{columns} "
   
     add_order!(sql, options[:order], scope)
     add_limit!(sql, options, scope)
     add_lock!(sql, options, scope)
   
     find_by_sql(sql)
   end
 
   def self.parse_tags(tags)
     return [] if tags.blank?
     tags = Array(tags).first
     tags = tags.respond_to?(:flatten) ? tags.flatten : tags.split(MetaTag::DELIMITER)
     tags.map { |tag| tag.strip.squeeze(" ") }.flatten.compact.uniq
   end

end