Рубиновые блоки с method_missing - PullRequest
1 голос
/ 21 апреля 2011

Обратите внимание, это продолжение моего вопроса здесь .

Я пытаюсь разобрать следующий код 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 ]

1 голос
/ 22 апреля 2011

Вы можете сделать (я пометил строки, которые я изменил):

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

Таким образом, блок будет существовать.

1 голос
/ 21 апреля 2011

Это не имеет ничего общего с method_missing. Вы просто не можете опустить скобки при передаче блока вместе с некоторыми параметрами. В вашем случае Ruby попытается вызвать метод 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>'
0 голосов
/ 21 апреля 2011

Так что лучшее решение, которое я нашел, это просто gsub строка перед обработкой.

tcl = <<-TCL.gsub(/^\s+/, "").chop.gsub('{', 'do').gsub('}', 'end')
  foo bar {
    biz buzz
  }
TCL
...