2009-09-30 63 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作爲可靠合同的價值。

相反,考慮將輸入轉換爲更接近接收點。