Den*_*rdy 7 ruby metaprogramming
我正在玩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
Run Code Online (Sandbox Code Playgroud)
三个问题:
有没有办法在不重命名方法的情况下这样做,以便允许使用super?还是更清洁/更短的东西?
有没有一种干净的方法来设置默认值?
有没有办法wrap :bar进一步推动通话?
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
Run Code Online (Sandbox Code Playgroud)
据我所知,没有重命名就无法实现这一目标.你可以试着super在define_method街区内打电话.但首先,如果明确指定参数,则只能super在一个define_method遗嘱中调用,否则会收到错误.但即使你打电话给eg super(*args),self在那种情况下也会成为Foo的一个例子.因此,调用bar将转到超类Foo,而不是找到并最终导致错误.
2)是的,就像这样
define_method method do |def_val='foo', *rest|
send(old, def_val, *rest)
end
Run Code Online (Sandbox Code Playgroud)
但是,在Ruby 1.8中,不可能使用块define_method,但这已经修复为1.9.如果您使用1.9,您也可以使用它
define_method method do |def_val='foo', *rest, &block|
send(old, def_val, *rest, &block)
end
Run Code Online (Sandbox Code Playgroud)
3)不,不幸的是.alias_method需要存在作为输入的方法.随着Ruby方法在解析时出现,wrap调用必须在定义之后放置,bar否则alias_method会引发异常.