2008-10-09 74 views
63

作为编程练习,我编写了一个创建类的Ruby片段,实例化该类中的两个对象,monkeypatches一个对象,并依靠method_missing来修改另一个对象。Ruby:define_method与def

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

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一个参数。 ...

回答

132

define_method是对象类别的(私有)方法。您从实例调用它。没有实例方法define_method,因此它会递归到您的method_missing,这次是:define_method(缺少方法的名称)和:screech(您传递给define_method的唯一参数)。

试试这个(定义上的所有猴子对象的新方法):

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 

还是这个(定义它只能在它被要求的对象,使用对象的“eigenclass”):

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

这是一个很好的答案,Avdi,它清除了我有的其他问题。谢谢。 – gauth 2008-10-09 11:40:11

4

self.class.define_method(:尖叫)是不行的,因为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 
4
def method_missing(m) 
    self.class.class_exec do 
     define_method(:screech) {puts "This is the new screech."} 
    end 
end 

screech方法将可用于所有猴子对象。