改个bug

问题见: http://yuan.iteye.com/blog/575569#comments 2楼
下面是过程。
入口是tag_list=
grep 'def tag_list=' ./ -r

在vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb文件中。
定义在ActiveRecord::Acts:Taggable::InstanceMethods里。只有一行代码:
def tag_list=(value)
  @tag_list = TagList.from(value)
end

接着找class TagList:
grep 'class TagList' ./ -r

位置:vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb
  class << self
    # Returns a new TagList using the given tag string.
    #   
    #   tag_list = TagList.from("One , Two,  Three")
    #   tag_list # ["One", "Two", "Three"]
    def from(source)
      returning new do |tag_list|
     
        case source
          when Array
            tag_list.add(source)
          else
            string = source.to_s.dup
     
            # Parse the quoted tags
            [   
              /\s*#{delimiter}\s*(['"])(.*?)\1\s*/,
              /^\s*(['"])(.*?)\1\s*#{delimiter}?/
            ].each do |re|
              string.gsub!(re) { tag_list << $2; "" }
            end 
     
            tag_list.add(string.split(delimiter))#分隔符就在这处理了
        end 
      end 
    end 
  end 

add方法:
  # Add tags to the tag_list. Duplicate or blank tags will be ignored.
  #
  #   tag_list.add("Fun", "Happy")
  # 
  # Use the <tt>:parse</tt> option to add an unparsed tag string.
  #
  #   tag_list.add("Fun, Happy", :parse => true)
  def add(*names)
    extract_and_apply_options!(names)
    concat(names)
    clean!
    self
  end

嗯,原来add可以带个参数:parse=> false|true。
怎么没看到数据库操作……哦,对了,调用tag_list=的时候不保存的。应该是在save的时候。
Module ClassMethods
#................
  after_create :save_tags
  after_update :save_tags
#.............
end

Module InstanceMethods
#......
  def save_tags
    return unless @tag_list

    new_tag_names = @tag_list - tags.map(&:name)
    old_tags = tags.reject { |tag| @tag_list.include?(tag.name) }

    self.class.transaction do
      if old_tags.any?
        taggings.find(:all, :conditions => ["tag_id IN (?)", old_tags.map(&:id)]).each(&:destroy)
        taggings.reset
      end

      new_tag_names.each do |new_tag_name|
        tags << Tag.find_or_create_with_like_by_name(new_tag_name)
      end
    end
    true
  end
#......
end

嗯,在transaction里边加点东西:
  self.class.transaction do
    if old_tags.any?
      taggings.find(:all, :conditions => ["tag_id IN (?)", old_tags.map(&:id)]).each(&:destroy)
      taggings.reset
      Tag.clear_unused old_tags ####加这么一句
    end 
                
    new_tag_names.each do |new_tag_name|
      tags << Tag.find_or_create_with_like_by_name(new_tag_name)
    end 
  end 

然后找到Tag类,添加一个方法:
class Tag < ActiveRecord::Base
  #......
  class << self
    #......
    def clear_unused(tags=[])
      if tags.empty?
        find(:all).select{|tag| tag.taggings.empty?}.each(&:destroy)
      else
        find(:all, :conditions => ['id IN (?)', tags.map(&:id)]).select{|tag| tag.taggings.empty?}.each(&:destroy)
      end 
    end 

    #......
  end
  #......
end

大功告成。
代码不知道还能改好看些不

你可能感兴趣的:(Blog,Ruby,ActiveRecord,idea)