This week I found out that there are important differences between Ruby and Groovy when it comes to evaluating 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:

say "Hello"

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.

def say(message)
 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:

class Context
  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:

def say(message){
  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:

Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: Script1.say() is applicable for argument types: (java.lang.String) values: {"Hello"}

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:

def say(message){ 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 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:

class Context{ 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 would 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).