define_method多次使用相同的名称

时间:2017-03-20 22:36:30

标签: ruby

我已在method_missing上写了一个小包装:

module Util
  def method_missing_for(regex, &blk1)
    define_method(:method_missing) do |name, *args, &blk2|
      match = name.to_s.scan(regex).flatten[0]
      if match
        blk1.call(match, *args, &blk2)
      else
        super(name, *args, &blk2)
      end
    end
  end
end

然后使用它:

class Foo
  extend Util
  method_missing_for(/^say_(.+)$/){ |word| puts word }
end

Foo.new.say_hello
# => "hello"

问题是我不能多次为一个课程调用它。我添加method_missing的{​​{1}}只会被覆盖。我有什么选择?从概念上讲,我知道我可以重构define_method以获取多个正则表达式=>阻止映射,然后调用一次而不是多次。它的核心是一个大案例陈述,它测试所有的正则表达式。但我宁愿能够利用method_missing_for

super

2 个答案:

答案 0 :(得分:0)

我的想法是在声明时注册定义,并在完成所有声明时定义这些方法。用法会略有不同,但我认为仍然可以接受

class Foo
  extend Util

  phantom_methods do
    method_missing_for(/^say_(.+)$/){ |word| puts word }
    method_missing_for(/^foo_(.+)$/){ |word| puts word }
  end
end

Util的实施将是

module Util
  def phantom_methods
    # Initialize the registration table
    @_phantom_methods = {}

    # Allow declaring methods
    yield

    # Consume the registration table and define a single `method_missing`
    define_method(:method_missing) do |name, *args, &blk|
      pair = self.class.instance_variable_get(:@_phantom_methods).find {|regexp, prc| name =~ regexp}
      pair ? pair[1].call($1) : super(name, *args, &blk)
    end

    # Good practice to also define `respond_to_missing?`
    define_method(:respond_to_missing?) do |name, include_private = false|
      self.class.instance_variable_get(:@_phantom_methods).any? {|regexp, _| name =~ regexp}
    end
  end

  # Declare a phantom method
  def method_missing_for(regexp, &blk)
    # Register the definition
    @_phantom_methods[regexp] = blk
  end
end

顺便说一下,我从this book借用了幻影方法这个短语。

答案 1 :(得分:0)

  

问题是我不能多次为一个课程调用它。我添加method_missing的{​​{1}}只会被覆盖。

不,它没有。它完成了

你已经有了这句话的解决方案:你需要而不是:

define_method

注意:命名模块可能有用也可能没有用,因此它在祖先链中显示了一个合理的名称,从而改善了调试体验:

module Util
  def method_missing_for(regex, &blk1)
    prepend(Module.new do
    ##################### this is the only change compared to your code
      define_method(:method_missing) do |name, *args, &blk2|
        match = name.to_s.scan(regex).flatten[0]
        if match
          blk1.(match, *args, &blk2)
        else
          super(name, *args, &blk2)
        end
      end
    end)
    ####
  end
end

class Foo
  extend Util
  method_missing_for(/^say_(.+)$/) { |word| puts word }
  method_missing_for(/foobar/) {}
end
Foo.new.say_hello
# hello

另见When monkey patching a method, can you call the overridden method from the new implementation?更全面的治疗方法。