扩展模块中的类方法

时间:2011-07-08 18:42:05

标签: 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

三个问题:

  1. 如果不重命名方法,有没有办法做到这一点,以便允许使用super?或者更清洁/更短的东西?

  2. 有没有简洁的方法来设置默认值?

  3. 有没有办法进一步推动wrap :bar来电?

1 个答案:

答案 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}}会引发异常。