带有method_missing的Ruby块

时间:2011-04-21 05:23:32

标签: ruby parsing tcl dsl method-missing

请注意,这是对我的问题here 的跟进。

我正在尝试解析以下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 个答案:

答案 0 :(得分:1)

这与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>'

答案 1 :(得分:1)

你可以这样做(我标记了我改变的行):

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

那样,块就会存在。

答案 2 :(得分:0)

因此,我发现最好的解决方案是在处理字符串之前只需gsub字符串。

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