在Ruby中,有没有办法在调用类的任何其他方法时调用方法? 例如,
class Car
def repair
puts "Repaired!"
end
def drive
# content
end
def checkup
# content
end
end
在这个例子中,如果我在Car
的实例上调用任何方法,我应该总是调用repair
方法。我如何在Ruby中做到这一点?
注意:我也做想要在内置方法中调用repair
,例如Carinstance.class
应该调用repair
也是。
答案 0 :(得分:7)
我假设您希望在每个Car#repair
的其他实例方法返回后调用Car
。我看到你已经添加了一个要求,其他方法也会调用repair
。我最后添加了一些关于将其扩展为包含内置实例方法的评论。
我采用的方法是使用BasicObject#method_missing:
class Car
def repair
puts "Repaired!"
end
def drive
puts "Drive!"
end
def checkup
puts "Checkup!"
end
def method_missing(m, *args)
if @@ims.key?(m)
ret = send(@@ims[m], *args)
repair
ret
else
super
end
end
@@ims = instance_methods(false).each_with_object({}) do |m,h|
next if (m == :repair || m == :method_missing)
saved_name = "_#{m}"
alias_method saved_name, m
h[m] = saved_name
remove_method(m)
end
end
car = Car.new
car.repair
Repaired!
car.drive
Drive!
Repaired!
car.checkup #
Checkup!
Repaired!
car.wash # => in `method_missing': undefined method `wash'...
解析类Car
后,在构造完所有实例方法之后,执行以下操作,我用一个例子解释:
instance_methods(false) # => [:repair, :drive, :checkup, :method_missing]
each_with_object({})
创建一个哈希(最初为空),由块变量h
引用(稍后将详细介绍)。
next if (m == :repair || m == :method_missing)
导致:repair
和:method_missing
被跳过。
当m => :drive
时,以下三个语句有效地将:drive
重命名为:_drive
,并将:drive" => "_drive"
添加到哈希h
。
each_with_object
返回
@@ims = {:drive=>"_drive", :checkup=>"_checkup"}
现在
instance_methods(false) # => [:repair, :method_missing, :_drive, :_checkup]
由于不再使用方法:drive
,Car.new.drive
会调用method_missing(:drive)
。后者发现@@ims
有一个键:drive
,因此它使用send
来调用:_drive
,调用:repair
并返回:_drive
的返回值}。如果method_missing
传递的方法不是@@ims
的密钥,则会调用super
并引发异常。
在一个现已删除的编辑中,我建议要包含内置实例方法,只需将instance_methods(false)
更改为instance_methods
,但警告可能出现意外的副作用。 @Kal指出无法删除内置实例方法,因此该方法不起作用。这也是一样的 - 不应该以这种方式混淆Ruby。我显然没有测试我的断言。耻辱!
答案 1 :(得分:1)
class Car
def self.default_method
instance_methods(true).each do |meth|
alias_method meth, :repair
end
end
def initialize
self.class.default_method
end
def repair
puts "Repaired!"
end
def drive
# content
end
def checkup
# content
end
end
car = Car.new
car.drive # => Repaired!
car.checkup # => Repaired!
car.class # => Repaired!
请注意,重新定义内置方法会产生一些警告:
# => untitled 5:6: warning: redefining `object_id' may cause serious problems
# => untitled 5:6: warning: redefining `__send__' may cause serious problems
编辑:糟糕,我发布的内容太快,没有发现问题。它调用repair
,但不调用原始方法。我知道这似乎太容易了!我想我对这个问题不够深入。 :-)(注意:我认为Cary的方法非常聪明,它确实适用于您自己的方法,但看起来他使用内置方法达到了死胡同,无论如何,它改变方法的方式你真的不应该尝试使用内置方法。)