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:
我正在玩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
Three questions:
-
Is there any way to do this without renaming the method, so as to allow the use of
super
? Or something cleaner/shorter?有没有办法在不重命名方法的情况下这样做,以便允许使用super?还是更清洁/更短的东西?
-
Is there a clean way to set the default values?
有没有一种简洁的方法来设置默认值?
-
Is there a means to move the
wrap :bar
call further up?有没有办法移动wrap:bar进一步调用?
1 个解决方案
#1
6
1) Cleaner/shorter
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
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.
据我所知,没有重命名就无法实现这一目标。您可以尝试在define_method块中调用super。但首先,如果明确指定参数,则只能在define_method内调用super,否则会收到错误。但即使你打电话,例如super(* args),在该上下文中的self将是Foo的一个实例。所以对bar的调用会转到Foo的超类,找不到并最终导致错误。
2) Yes, like so
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
但是,在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) 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.
3)不,不幸的是。 alias_method需要存在它作为输入的方法。由于Ruby方法在解析时就已存在,因此必须在bar的定义之后放置wrap调用,否则alias_method会引发异常。
#1
6
1) Cleaner/shorter
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
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.
据我所知,没有重命名就无法实现这一目标。您可以尝试在define_method块中调用super。但首先,如果明确指定参数,则只能在define_method内调用super,否则会收到错误。但即使你打电话,例如super(* args),在该上下文中的self将是Foo的一个实例。所以对bar的调用会转到Foo的超类,找不到并最终导致错误。
2) Yes, like so
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
但是,在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) 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.
3)不,不幸的是。 alias_method需要存在它作为输入的方法。由于Ruby方法在解析时就已存在,因此必须在bar的定义之后放置wrap调用,否则alias_method会引发异常。