在模块中扩展类方法 [英] Extending a class method in a module
问题描述
我正在使用ruby的元编程功能,但发现它有点毛茸茸.我正在尝试使用模块包装方法调用.目前,我正在这样做:
I'm playing with ruby's metaprogramming features, and I'm finding it a bit hairy. I'm trying to wrap a method call using a module. Currently, I'm doing this:
module Bar
module ClassMethods
def wrap(method)
class_eval do
old_method = "wrapped_#{method}".to_sym
unless respond_to? old_method
alias_method old_method, method
define_method method do |*args|
send old_method, *args
end
end
end
end
end
def self.included(base)
base.extend ClassMethods
end
end
class Foo
include Bar
def bar(arg = 'foo')
puts arg
end
wrap :bar
end
三个问题:
-
是否可以在不重命名方法的情况下执行此操作,以便允许使用
super
?还是更清洁或更短的东西?
Is there any way to do this without renaming the method, so as to allow the use of
super
? Or something cleaner/shorter?
是否有一种干净的方法来设置默认值?
Is there a clean way to set the default values?
是否可以将wrap :bar
调用进一步向上移动?
Is there a means to move the wrap :bar
call further up?
推荐答案
1)清洁/简短
module ClassMethods
def wrap(method)
old = "_#{method}".to_sym
alias_method old, method
define_method method do |*args|
send(old, *args)
end
end
end
class Foo
extend ClassMethods
def bar(arg = 'foo')
puts arg
end
wrap :bar
end
据我所知,没有重命名就无法实现这一目标.您可以尝试在define_method
块中调用super
.但是首先,只有在显式指定参数的情况下,从define_method
内对super
的调用才会成功,否则会出现错误.但是即使你打电话给在这种情况下,super(*args)
和self
将是Foo的一个实例.因此,对bar
的调用将转到Foo
的超类,但未找到它们并最终导致错误.
As far as I know there is no way to achieve this without renaming. You could try to call super
inside the define_method
block. But first of all, a call to super
from within a define_method
will only succeed if you specify arguments explicitly, otherwise you receive an error. But even if you call e.g. super(*args)
, self
in that context would be an instance of Foo. So a call to bar
would go to the super classes of Foo
, not be found and ultimately result in an error.
2)是的,像这样
define_method method do |def_val='foo', *rest|
send(old, def_val, *rest)
end
However, in Ruby 1.8 it is not possible to use a block in define_method
, but this has been fixed for 1.9. If you are using 1.9, you could also use this
define_method method do |def_val='foo', *rest, &block|
send(old, def_val, *rest, &block)
end
3)不幸的是. alias_method
要求存在作为输入的方法.由于Ruby方法在解析时就存在,因此wrap
调用必须放在bar
的定义之后,否则alias_method
会引发异常.
3) No, unfortunately. alias_method
requires the existence of the methods that it takes as input. As Ruby methods come into existence as they are parsed, the wrap
call must be placed after the definition of bar
otherwise alias_method
would raise an exception.
这篇关于在模块中扩展类方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!