Рубиновые блоки с method_missing
Обратите внимание, это продолжение моего вопроса здесь.
Я пытаюсь разобрать следующий код Tcl:
foo bar {
biz buzz
}
В Tcl foo
это имя метода, bar
является аргументом, а остальное является "блоком" для обработки eval
,
Теперь вот моя текущая реализация этого:
def self.foo(name, &block)
puts "Foo --> #{name}"
if block
puts "block exists"
else
puts "block does not exist"
end
end
def self.method_missing(meth, *args, &block)
p meth
p block
meth.to_s &block
end
tcl = <<-TCL.gsub(/^\s+/, "").chop
foo bar {
biz buzz
}
TCL
instance_eval(tcl)
Который выводит следующее:
:bar
#<Proc:0x9e39c80@(eval):1>
Foo --> bar
block does not exist
В этом примере, когда блок передается до foo
Метод, он не существует. Еще в method_missing
оно существует (по крайней мере, оно существует). Что тут происходит?
Обратите внимание, я знаю приоритет скобок в Ruby и понимаю, что это работает:
foo (bar) {
biz buzz
}
Тем не менее, я хочу опустить скобки. Так возможно ли это в рубине (без лексического анализа)?
3 ответа
Вы можете сделать (я пометил строки, которые я изменил):
def self.foo args # changed
name, block = *args # changed
puts "Foo --> #{name}"
if block
puts "block exists"
else
puts "block does not exist"
end
end
def self.method_missing(meth, *args, &block)
p meth
p block
return meth.to_s, block # changed
end
Таким образом, блок будет существовать.
Это не имеет ничего общего с method_missing
, Вы просто не можете опустить скобки при передаче блока вместе с некоторыми параметрами. В вашем случае Руби попробует позвонить bar
метод с блоком в качестве аргумента, и результат всего этого будет передан foo
метод в качестве одного аргумента.
Вы можете попробовать это сами, упростив вызов метода (все метапрограммирование просто скрывает реальную проблему в вашем случае):
# make a method which would take anything
def a *args, &block
end
# try to call it both with argument and a block:
a 3 {
4
}
#=>SyntaxError: (irb):16: syntax error, unexpected '{', expecting $end
# from /usr/bin/irb:12:in `<main>'
Так что лучшее решение, которое я нашел, это просто gsub
строка перед обработкой.
tcl = <<-TCL.gsub(/^\s+/, "").chop.gsub('{', 'do').gsub('}', 'end')
foo bar {
biz buzz
}
TCL