在模块中扩展类方法 [英] Extending a class method in a module

查看:68
本文介绍了在模块中扩展类方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用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

三个问题:

  1. 是否可以在不重命名方法的情况下执行此操作,以便允许使用super?还是更清洁或更短的东西?

  1. 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

但是,在Ruby 1.8中,它是

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆