2008-10-09 3 views
63

Как упражнение по программированию, я написал фрагмент Ruby, который создает класс, создает два объекта из этого класса, обезьяна передает один объект и полагается на метод_missing, чтобы обезвредить другой.Ruby: define_method vs. 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

Это отличный ответ, Авди, и он проясняет некоторые другие вопросы, которые у меня были. Спасибо. – 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 будет доступен для всех объектов Monkey.

Смежные вопросы