Evaluating code dynamically in Groovy (differences with Ruby)
2009-10-10This week I found out that there are important differences between Ruby and Groovy when it comes to evaluate code dynamically. In order to write the testing infrastructure of a DSL we were developing, we wanted to invoke the code contained in isolated files in the context of the caller code. We were using Groovy, and I wrongly assumed that it would be as easy as with Ruby.
Imagine you have a file named say.script with the next contents:
In this DSL, you can say things that are visualized in the STDOUT. Executing this script with Ruby is very simple: just invoke eval() with the code to evaluate it in a context where the say() method exists.
puts message
end
eval File.read('say.script')
If you want to evaluate the code in the context of some object (for example, an expression builder), you can write something like:
def say(message)
puts message
end
end
context = Context.new
code = File.read('say.script')
context.instance_eval code
However, in Groovy, the GroovyShell.evaluate() method is not equivalent to Ruby’s eval() method. While both mechanisms can share state between the caller and the invoked code through the concept of bindings, in Groovy the evaluate() method is always resolved in the context of a Script object. This means that the implicit invocation context is not shared. If you try to execute this code:
println message
}
def shell = new GroovyShell()
def code = new File("say.script").text
shell.evaluate(code) //WRONG: in the script object the say() method is not defined
You will obtain something like:
A workaround for this situation is to convert the script code into a closure. This way, the DSL code is evaluated but not executed. With the closure object you can make the invocation in the context you prefer. In the last example, code bellow will make it:
println message
}
def shell = new GroovyShell()
def code = new File("say.script").text
code = "{->${code}}"
def closure = shell.evaluate(code)
closure.delegate=this
closure()
In the example, we wrap the code to execute with a closure syntax. In this way, when we call evaluate we obtain a closure object. With this closure object we can modify its delegate (invocation context) and then call it.
Finally, if we wanted to execute the code using a specific invocation context (something like Ruby’s instance_eval), we just have to modify the delegate object:
def say(message){
println message
}
}
def shell = new GroovyShell()
def code = new File("say.script").text
code = "{->${code}}"
def context = new Context()
def closure = shell.evaluate(code)
closure.delegate = context
closure()
I don’t know of a better approach for solving this problem with Groovy. Initially I thought there will be a direct way, like in Ruby. But after hours of searching for it we didn’t find anything (which doesn’t mean it doesn’t exist, of course).

There are 2 comments in this article: