Groovy脚本如何与propertyMissing交互?

时间:2011-09-23 04:44:57

标签: groovy

我编写了一个以Script执行的DSL;它有各种各样的语法位。例如,对于采用闭包的“foo”关键字,我有一个FooSyntax类,并使用该语法的实例来评估闭包。这很好,例如。

bar = thing {} // make a thing 
baz = foo {
    mykeyword bar
}

将名为bar的东西传递给FooSyntax#mykeyword的调用。

我正在尝试在存在未知变量引用时添加一些更好的错误消息。这表示为MissingPropertyException,因此我目前的方法是向propertyMissing添加FooSyntax方法。这确实适用于缺失的变量。

不幸的是,它打破了上面的例子:bar成为缺失的属性,而不是落入Binding。为什么添加propertyMissing导致Binding无法咨询? (这是否与Closure的解决策略有关?)我该如何解决这个问题?

您可以使用https://gist.github.com/1237768

上的示例脚本来玩这个

2 个答案:

答案 0 :(得分:2)

我将把我的答案委托给我评论的要点。基本上,您不应该使用with()方法对FooSyntax委托执行闭包。为了将来参考,标准方法是:

def foo(Closure cl) {
    def f = new FooSyntax()
    def c = cl.clone()
    c.delegate = f
    c.call()
}

您可以通过更改闭包上的解析策略来微调行为,如下所示:

c.resolveStrategy = Closure.DELEGATE_FIRST

但在这种情况下,您需要默认的Closure.OWNER_FIRST以确保首先查询绑定。

答案 1 :(得分:0)

由于 bar 未被声明(刚刚分配),因此缺少该属性,因为脚本或语法类中没有已定义的栏。

在您的示例中,我认为您要实现 methodMissing 。在您的方案中,您尝试使用非Thing类型调用Foo.myKeyword。所以这真的是一种缺失的方法,而不是缺少属性。

我已经更改了你的脚本,将propertyMissing更改为methodMissing并为foo添加def,并将bar定义为String。

class Thing { }

class FooSyntax {
   def myKeyword(Thing t) { println "Hello Foo " + t.toString(); }    
   def methodMissing(String name, args) {
       println "no method named ${name} for ${args} exists"
   }
}

class ScriptSyntax {
    def foo(Closure cl) {
        def f = new FooSyntax();
        f.with cl
    }
    def thing() { new Thing()  }

    def dispatchKeyword(String methodName, Object args) {
       invokeMethod(methodName, args)
    }
}

def runner(String text) {
   def source = new GroovyCodeSource(text, "inlineScript", "inline.groovy")

   def script = new GroovyClassLoader().parseClass(source).newInstance(binding) as Script
   def dsl = new ScriptSyntax()
   script.metaClass.methodMissing = { name, args -> dsl.dispatchKeyword(name, args) }
   script.run()
}

runner("""def bar = thing()
def baz = "not a thing"
foo { myKeyword bar }
foo { myKeyword baz }""")

Output:
Hello Foo Thing@1038de7
no method named myKeyword for [not a thing] exists