2008-11-13 16 views
9

J'ai trouvé une source qui l'emportaient avec succès Time.strftime comme ceci:Comment - sans héritage - remplacer une méthode de classe et appeler l'original à partir de la nouvelle méthode?

class Time 
    alias :old_strftime :strftime 
    def strftime 
    #do something 
    old_strftime 
    end 
end 

Le problème est, strftime est une méthode d'instance. Je dois remplacer Time.now - une méthode de classe - de telle manière que tout appelant obtienne ma nouvelle méthode, tandis que la nouvelle méthode appelle toujours la méthode originale .now. J'ai regardé alias_method et n'ai rencontré aucun succès.

Répondre

11

C'est un peu difficile à obtenir autour de votre tête parfois, mais vous devez ouvrir le « eigenclass » qui est le singleton associé à un objet de classe spécifique . la syntaxe pour cela est la classe < < self do ... end.

class Time 
    alias :old_strftime :strftime 

    def strftime 
    puts "got here" 
    old_strftime 
    end 
end 

class Time 
    class << self 
    alias :old_now :now 
    def now 
     puts "got here too" 
     old_now 
    end 
    end 
end 

t = Time.now 
puts t.strftime 
2

Les méthodes de classe sont simplement des méthodes. Je recommande fortement contre cela, mais vous avez deux choix équivalents:

class Time 
    class << self 
    alias_method :old_time_now, :now 

    def now 
     my_now = old_time_now 
     # new code 
     my_now 
    end 
    end 
end 

class << Time 
    alias_method :old_time_now, :now 

    def now 
    my_now = old_time_now 
    # new code 
    my_now 
    end 
end 
1

Si vous devez le remplacer pour des fins de test (la raison pour laquelle je veux normalement passer outre Time.now), Ruby se moquant/cadres stubbing le feront pour vous facilement. Par exemple, avec RSpec (qui utilise flexmock):

Time.stub!(:now).and_return(Time.mktime(1970,1,1)) 

D'ailleurs, je vous recommande vivement d'éviter la nécessité de bouchonner Time.now en donnant vos classes une horloge dérogation possible:

class Foo 
    def initialize(clock=Time) 
    @clock = clock 
    end 

    def do_something 
    time = @clock.now 
    # ... 
    end 
end 
0

I J'ai essayé de comprendre comment remplacer une méthode d'instance en utilisant des modules.

module Mo 
    def self.included(base) 
    base.instance_eval do 
     alias :old_time_now :now 
     def now 
     my_now = old_time_now 
     puts 'overrided now' 
     # new code 
     my_now 
     end 
    end 
    end 
end 
Time.send(:include, Mo) unless Time.include?(Mo) 

> Time.now 
overrided now 
=> Mon Aug 02 23:12:31 -0500 2010