Ruby: define_method vs. def
Как упражнение по программированию, я написал фрагмент Ruby, который создает класс, создает два объекта из этого класса, обезьяна передает один объект и полагается на метод_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
...
Ответы
Ответ 1
define_method
- это (частный) метод класса объектов. Вы вызываете его из экземпляра. Нет метода экземпляра с именем 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
Или это (чтобы определить его только для объекта, на который он вызван, используя объект "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
Ответ 2
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
Ответ 3
def method_missing(m)
self.class.class_exec do
define_method(:screech) {puts "This is the new screech."}
end
end
метод screech будет доступен для всех объектов Monkey.