2009-09-30 65 views
0

我试图重新定义File.dirname方法,首先将%20s更改为空格。但下面给我一个错误重新定义File :: dirname ruby​​方法

class File 
    old_dirname = instance_method(:dirname)  

    define_method(:dirname) { |s| 
     s = s.gsub("%20"," ") 
     old_dirname.bind(self).call(s) 
    } 
end 

这trhows一个NameError异常:未定义的方法“目录名称”类“文件”

什么是做到这一点的正确方法?

回答

4

正如查克已经写,File::dirnameFile类对象(或者更准确地说是File类对象的元类的实例方法)的单方法,而不是File类的实例方法。

所以,你必须开拓File的元类,而不是File类本身:

#!/usr/bin/env ruby 

class << File 
    old_dirname = instance_method :dirname 

    define_method :dirname do |*args| 
    old_dirname.bind(self).(*args).gsub '%20', ' ' 
    end 
end 

require 'test/unit' 
class TestFileDirname < Test::Unit::TestCase 
    def test_that_it_converts_percent20_to_space 
    assert_equal '/foo bar/baz', File.dirname('/foo%20bar/baz/quux.txt') 
    end 
end 

不过,我同意@sheldonh:这打破了File::dirname的API合同。

1

dirname是一个File类的方法,而不是一个实例方法,所以你只是定义一个新的实例方法。此外,用于别名方法的惯用方法是alias。所以:

class <<File 
    alias old_dirname dirname 
    def dirname(f) 
    old_dirname(f.gsub("%20", " ")) 
    end 
end 

class <<whatever语法将方法单个对象 - 在这种情况下,文件类。

+0

感谢您的帮助 – jrhicks 2009-09-30 04:04:56

+2

这是*不相当于代码@jrhicks张贴在他的问题!该代码使用剩余的'File :: old_dirname'方法污染了'File'元类的名称空间,而问题中的原始代码非常小心地避免了这个问题。 – 2009-09-30 10:05:28

+0

Chuck对范围的讨论是完全正确的,但Jörg说得对:'alias_method'可能是“惯用的”,但它绝对不如OP的原始方法安全。 – 2009-09-30 12:45:32

3

只是要小心。

您正在改变方法的行为,而不仅仅是它的实现。这通常是不好的做法,因为它削弱了API作为可靠合同的价值。

相反,考虑将输入转换为更接近接收点。