我想知道在改变现有方法的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。这可能吗?您对如何改进这种修补/扩展方法有任何进一步的建议吗?或者甚至可能是完全不同的方法?
答案 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