什么是在ruby中扩展/修补库的DRIest方法?

时间:2012-02-13 17:23:14

标签: ruby dry quirks-mode class-extensions

我想知道在改变现有方法的ruby中编写现有库的模块化扩展的最佳方法是什么。它不应该引入重复的代码,只能按需使用。

我试图完成的具体任务是扩展ruby的Net::FTP模块以支持某些not so standards compliant servers。这样的扩展应该与符合标准的图书馆恕我直言。

完全分开

我认为要求一个额外的文件会非常好,因为这甚至不需要在原始代码中进行某种切换。因此,额外的require 'net/ftp/forgiving'将使原始库对我们那些不那么有天赋的FTP服务器研究员更加宽容。

然后,相关文件可以使用ruby的开放类和模块体系结构来修补FTP类。为了解决上面链接的古怪行为的例子,我需要修补Net::FTP#mkdir。看起来像这样:

#content of net/ftp/forgiving
require 'net/ftp'

module Net
  class FTP

    # mkdir that will accept a '250 Directory created' as a valid response
    def mkdir(dirname)
      begin
        original_mkdir(dirname)
      rescue FTPReplyError => e
        raise unless e.message.start_with? '250 Directory created'
        return ""
      end
    end

  end
end

然而,这需要以某种方式将原始Net::FTP#mkdir缓存为Net::FTP#original_mkdir以保持代码DRY。这可能吗?您对如何改进这种修补/扩展方法有任何进一步的建议吗?或者甚至可能是完全不同的方法?

1 个答案:

答案 0 :(得分:4)

这称为“monkeypatching”,正是alias_method的用例:

alias_method :original_mkdir, :mkdir
def mkdir(dirname)
  begin
    original_mkdir(dirname)
  rescue FTPReplyError => e
    raise unless e.message.start_with? '250 Directory created'
    return ""
  end
end

虽然这是Ruby中经常出现的“成语”,但会破坏现有代码(甚至可能是代码Net内部的代码)依赖mkdir引发异常在这种情况下。您不能将这些更改限制为仅require 'net/ftp/forgiving'的文件。因此,创建子类而不是打开原始类会更清晰:

module Net
  class ForgivingFTP < FTP
    # mkdir that will accept a '250 Directory created' as a valid response
    def mkdir(dirname)
      begin
        super(dirname)
      rescue FTPReplyError => e
        raise unless e.message.start_with? '250 Directory created'
        return ""
      end
    end
  end
end

甚至更好,将它放在自定义命名空间中!一个好的经验法则是:

  可能时的

子类,必要时的monkeypatch

(感谢@tadman为此)。在这种情况下,似乎没有必要。

更新:跟进您的评论,如果您只想扩展Net::FTP类的特定实例,则可以扩展其单例类:

obj = Net::FTP.new
class << obj
  alias_method :original_mkdir, :mkdir
  def mkdir(dirname)
    #...
    original_mkdir(dirname)
    #...
  end
end