扩展模块中的类方法
我正在玩ruby的元编程function,我发现它有点毛茸茸。 我正在尝试使用模块包装方法调用。 目前,我这样做:
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
进一步调用?
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
的超类,找不到并最终导致错误。
2)是的,就像这样
define_method method do |def_val='foo', *rest| send(old, def_val, *rest) end
但是,在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
3)不,不幸的是。 alias_method
需要存在它作为输入的方法。 由于Ruby方法在解析时就已存在,因此必须在bar
的定义之后放置wrap
调用,否则alias_method
会引发exception。