我正在玩ruby的元编程功能,我发现它有点毛茸茸。我正在尝试使用模块包装方法调用。目前,我这样做:
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
?或者更清洁/更短的东西?
有没有简洁的方法来设置默认值?
有没有办法进一步推动wrap :bar
来电?
答案 0 :(得分:6)
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
据我所知,没有重命名就无法实现这一目标。您可以尝试在super
块内调用define_method
。但首先,如果您明确指定参数,则只能在super
内调用define_method
,否则会收到错误。但即使你打电话,例如在此上下文中super(*args)
,self
将是Foo的一个实例。因此,对bar
的调用将转到Foo
的超类,找不到并最终导致错误。
2)是的,就像这样
define_method method do |def_val='foo', *rest|
send(old, def_val, *rest)
end
但是,在Ruby 1.8中define_method
使用define_method method do |def_val='foo', *rest, &block|
send(old, def_val, *rest, &block)
end
中的块是not possible,但这已经修复为1.9。如果您使用的是1.9,也可以使用此
alias_method
3)不,不幸的是。 wrap
要求存在作为输入的方法。由于Ruby方法在解析时就存在,bar
调用必须放在alias_method
的定义之后,否则{{1}}会引发异常。