扩展模块中的类方法

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)

三个问题:

  1. 有没有办法在不重命名方法的情况下这样做,以便允许使用super?还是更清洁/更短的东西?

  2. 有没有一种干净的方法来设置默认值?

  3. 有没有办法wrap :bar进一步推动通话?

emb*_*oss 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
Run Code Online (Sandbox Code Playgroud)

据我所知,没有重命名就无法实现这一目标.你可以试着superdefine_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会引发异常.