GStringの評価タイミングを勘違いしてた
GStringヒアドキュメント?で記述したスクリプトをGroovyShell#evaluateで評価しようとした時に、GString内のプレースホルダーが評価される場所を勘違いしていてハマったという話。
GString内のプレースホルダーはevaluateの前で評価される。
GString interpolation not working with GroovyShell ?
以下、経緯。
GroovyDSLを作ろうとしてGStringでハマった。
あとから要素を追加しやすいよう、methodMissingを使って動的にプロパティを追加できるようにしました。
def dsl = """\ person { name('Foo') age('28') } """ def script = """\ def person(Closure closure) { def person = new Person() closure.delegate = person closure() println person.profile() } class Person { Person() { def mc = new ExpandoMetaClass( Person, false, true) mc.initialize() this.metaClass = mc } // ここで動的にプロパティを追加したい def methodMissing(String name, args) { this.metaClass."${name}" = args[0] } def profile(){ this.properties } } """ new GroovyShell().evaluate("""\ ${dsl} ${script} """)
実行するとエラー。
Exception thrown groovy.lang.MissingPropertyException: No such property: methodName for class: ConsoleScript30 at ConsoleScript30.run(ConsoleScript30:8)
methodMissingに引数methodNameが上手く渡って無いのが原因かと思い、
def methodMissing(String methodName, args) { def methodName = 'hello' this.metaClass."${methodName}" = args[0] }
とかしてみたけど変わらず。
エラーの原因は一番上に書いた通りなので、変数dsl,scriptの宣言をGStringでなくシングルクォートのヒアドキュメントにしたら動きました。
def dsl = '''\ person { name('Foo') age('28') } ''' def script = '''\ def person(Closure closure) { def person = new Person() closure.delegate = person closure() println person.profile() } class Person { Person() { def mc = new ExpandoMetaClass( Person, false, true) mc.initialize() this.metaClass = mc } // ここで動的にプロパティを追加したい def methodMissing(String methodName, args) { this.metaClass."${methodName}" = args[0] } def profile(){ this.properties } } ''' new GroovyShell().evaluate("""\ ${dsl} ${script} """)
[class:class Person, age:28, name:Foo]