2009-09-30 8 views
0

J'essaye de redéfinir la méthode File.dirname pour changer d'abord% 20s en espaces. Mais ce qui suit me donne une erreurRedéfinir la méthode 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 

Cette trhows une exception NameError: méthode non définie « dirname » pour la catégorie « Fichier »

Quelle est la bonne façon de le faire?

Répondre

4

Comme Chuck déjà écrit, File::dirname est une méthode singleton de l'objet de classe File (ou plus précisément une méthode d'instance de la métaclasse d'un objet de classe File), pas une méthode d'instance de la classe File.

Ainsi, vous devez ouvrir métaclasse de File, pas la classe elle-même 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 

Cependant, je suis d'accord avec @sheldonh: ce rompt le contrat API de File::dirname.

1

dirname est une méthode de classe de File, pas une méthode d'instance, donc vous définissez simplement une nouvelle méthode d'instance. En outre, la façon idiomatique d'alias une méthode est avec alias. Alors:

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

La syntaxe class <<whatever ajoute des méthodes à un objet individuel - dans ce cas, la classe File.

+0

merci pour l'aide – jrhicks

+2

Ceci n'est * pas * l'équivalent du code @jrhicks posté dans sa question! Ce code pollue l'espace de nom de la métaclasse 'File' avec une méthode résiduelle' File :: old_dirname', alors que le code original dans la question prend soin d'éviter ce problème. –

+0

La discussion sur la portée est entièrement correcte, Chuck, mais Jörg a raison: 'alias_method' peut être" idiomatique ", mais c'est certainement moins sûr que l'approche originale de l'OP. –

3

Soyez prudent.

Vous modifiez le comportement de la méthode, pas seulement son implémentation. Cette pratique est généralement mauvaise, car elle affaiblit la valeur de l'API en tant que contrat fiable. Au lieu de cela, envisagez de transformer l'entrée plus près du point de réception.

Questions connexes