我编写了一个以Script
执行的DSL;它有各种各样的语法位。例如,对于采用闭包的“foo”关键字,我有一个FooSyntax
类,并使用该语法的实例来评估闭包。这很好,例如。
bar = thing {} // make a thing
baz = foo {
mykeyword bar
}
将名为bar的东西传递给FooSyntax#mykeyword
的调用。
我正在尝试在存在未知变量引用时添加一些更好的错误消息。这表示为MissingPropertyException
,因此我目前的方法是向propertyMissing
添加FooSyntax
方法。这确实适用于缺失的变量。
不幸的是,它打破了上面的例子:bar
成为缺失的属性,而不是落入Binding。为什么添加propertyMissing
导致Binding
无法咨询? (这是否与Closure
的解决策略有关?)我该如何解决这个问题?
答案 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