66

作为一个编程练习,我编写了一个 Ruby 片段,它创建一个类,从该类实例化两个对象,对一个对象进行猴子补丁,并依靠 method_missing 对另一个对象进行猴子补丁。

这是交易。这按预期工作:

class Monkey

  def chatter
    puts "I am a chattering monkey!"
  end

  def method_missing(m)
    puts "No #{m}, so I'll make one..."
    def screech
      puts "This is the new screech."
    end
  end
end

m1 = Monkey.new
m2 = Monkey.new

m1.chatter
m2.chatter

def m1.screech
  puts "Aaaaaargh!"
end

m1.screech
m2.screech
m2.screech
m1.screech
m2.screech

您会注意到我有一个method_missing 参数。我这样做是因为我希望使用 define_method 来动态创建具有适当名称的缺失方法。但是,它不起作用。事实上,即使使用带有静态名称的 define_method,如下所示:

def method_missing(m)
  puts "No #{m}, so I'll make one..."
  define_method(:screech) do
    puts "This is the new screech."
  end
end

以以下结果结束:

ArgumentError: wrong number of arguments (2 for 1)

method method_missing   in untitled document at line 9
method method_missing   in untitled document at line 9
at top level    in untitled document at line 26
Program exited.

使错误消息更令人困惑的是,我只有一个论点method_missing...

4

3 回答 3

140

define_method是对象Class的(私有)方法。您正在从一个实例调用它。没有调用实例方法define_method,所以它递归到你的method_missing,这次是:define_method(缺少的方法的名称)和:screech(你传递给的唯一参数define_method)。

试试这个(在所有 Monkey 对象上定义新方法):

def method_missing(m)
    puts "No #{m}, so I'll make one..."
    self.class.send(:define_method, :screech) do
      puts "This is the new screech."
    end
end

或者这个(只在它被调用的对象上定义它,使用对象的“特征类”):

def method_missing(m)
    puts "No #{m}, so I'll make one..."
    class << self
      define_method(:screech) do
        puts "This is the new screech."
      end
    end
end
于 2008-10-09T04:58:17.037 回答
4

self.class.define_method(:screech) 不起作用,因为 define_method 是私有方法,你可以这样做

class << self
    public :define_method
end
def method_missing(m)
puts "No #{m}, so I'll make one..."
Monkey.define_method(:screech) do
  puts "This is the new screech."
end
于 2008-10-28T02:56:20.513 回答
4
def method_missing(m)
    self.class.class_exec do
       define_method(:screech) {puts "This is the new screech."}
    end 
end

尖叫方法将适用于所有 Monkey 对象。

于 2010-08-27T04:46:01.967 回答