Reputation: 14418
This code:
evaluate ("def test() { println \"Test is successful!\" }")
test()
results in exception:
FATAL: No signature of method: script1409644336796288198097.test() is applicable for argument types: () values: [] Possible solutions: use([Ljava.lang.Object;), getAt(java.lang.String), use(java.util.List, groovy.lang.Closure), use(java.lang.Class, groovy.lang.Closure), wait(), wait(long) groovy.lang.MissingMethodException: No signature of method: script1409644336796288198097.test() is applicable for argument types: () values: [] Possible solutions: use([Ljava.lang.Object;), getAt(java.lang.String), use(java.util.List, groovy.lang.Closure), use(java.lang.Class, groovy.lang.Closure), wait(), wait(long) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:55) ...
What I'm doing wrong?
Upvotes: 12
Views: 20224
Reputation: 8695
In addition to all other answers, if you don't want to change the structure of your code you can simply use return this
at the end of your groovy-string:
lib = evaluate ("def test() { println \"Test is successful!\" }; return this")
lib.test()
Upvotes: 1
Reputation: 1060
If a variable has an undeclared type, then it goes into the script binding. The binding is visible to all methods which means data is shared.
evaluate()
is a helper method to allow the dynamic evaluation of groovy expressions using this
scripts binding as the variable scope.
In a variable binding, you can declare a closure which accepts no argument and must be restricted to calls without arguments.
With all of that in mind, here is your script working as intended.
evaluate ("test = { -> println \"Test is successful!\" }")
test()
Upvotes: 3
Reputation: 14529
That script evaluation results in null. You should either return something or execute the script and return the result.
An example returning a closure instead of defining a method:
test = evaluate ('return { "Test is successful!" }')
assert test() == "Test is successful!"
And an example where the script executes the method itself:
result = evaluate 'def test() { "eval test" }; return test()'
assert result == "eval test"
If you cannot change the script code, you may parse a class from the script, create a new object, and then execute the test()
method:
def parent = getClass().getClassLoader()
def loader = new GroovyClassLoader(parent)
def clazz = loader.parseClass('def test() { "new class definition" }');
obj = clazz.newInstance()
assert obj.test() == "new class definition"
Upvotes: 15
Reputation: 765
You can do this using the ExpandoMetaClass to add dynamic closures to your own class. You would need to parse the string beforehand to split it into function name, arguments and code.
methodName = "test"
methodArgs = []
methodCode = """println "Hello World!" """
this.metaClass."$methodName"{ code, args ->
evaluate(code)
}
Then you can call it by doing:
"$methodName"(code, arguments)
or
test(code, arguments)
To get the output Hello World!
You can read more about ExpandoMetaClass here http://groovy.codehaus.org/ExpandoMetaClass
Upvotes: 1