Comment peut-on - sans héritage - remplacer une méthode de classe et appeler l'original à partir de la nouvelle méthode?
-
08-07-2019 - |
Question
J'ai trouvé une source qui a remplacé avec succès Time.strftime
comme ceci:
class Time
alias :old_strftime :strftime
def strftime
#do something
old_strftime
end
end
Le problème est que strftime
est une méthode d'instance. Je dois remplacer Time.now
- une méthode de classe - de manière à ce que tout appelant reçoive ma nouvelle méthode, tandis que la nouvelle méthode appelle toujours la méthode .now
d'origine. J'ai consulté alias_method
et je n'ai rencontré aucun succès.
La solution
C’est un peu difficile à comprendre, mais vous devez ouvrir la & "eigenclass &"; qui est le singleton associé à un objet de classe spécifique. la syntaxe est la classe < < faire soi-même ... fin
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
Autres conseils
Les méthodes de classe ne sont que des méthodes. Je le recommande vivement, 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
Si vous devez le remplacer à des fins de test (la raison pour laquelle je souhaite généralement remplacer Time.now), les frameworks de moquage / stub de Ruby le feront facilement pour vous. Par exemple, avec RSpec (qui utilise flexmock):
Time.stub!(:now).and_return(Time.mktime(1970,1,1))
En passant, je vous recommande vivement d'éviter de remplacer Time.now en donnant à vos classes une horloge qui peut être remplacée:
class Foo
def initialize(clock=Time)
@clock = clock
end
def do_something
time = @clock.now
# ...
end
end
J'ai essayé de comprendre comment remplacer une méthode d'instance à l'aide de 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