I'm trying to generate a closure from a string. The code inside the closure references a DSL function build(). The errors I'm getting imply that Groovy is trying to execute the closure instead of just declaring it. What is the correct syntax for this? Here are some of the constructs I have already tried.
sh = new GroovyShell()
cl = sh.evaluate( '{ build("my job") }' }
=> Ambiguous expression could be either a parameterless closure expression or an isolated open code block;
sh = new GroovyShell()
cl = sh.evaluate( 'L: { build("my job") }' }
=> No signature of method: Script1.build() is applicable ...
cl = Eval.me( 'L: { build("my job") }' }
=> No signature of method: Script1.build() is applicable ...
cl = Eval.me( 'L: { com.flow.FlowDelegate.build("my job") }' }
=> No such property: com for class: Script1
The example I'm trying to follow comes from:
Load closure code from string in Groovy
What about returning the closure from the script?
Eval.me("return { build('my job') } ")
What do you intend using that L:? Returning a map? If is that so, you can use square brackets:
groovy:000> a = Eval.me("[L: { build('test for') }]")
===> {L=Script1$_run_closure1#958d49}
groovy:000> a.L
===> Script1$_run_closure1#958d49
Consider the example below. The key is to specify, explicitly, a closure without parameters.
def build = { def jobName ->
println "executing ${jobName}"
}
// we initialize the shell to complete the example
def sh = new GroovyShell()
sh.setVariable("build", build)
// note "->" to specify the closure
def cl = sh.evaluate(' { -> build("my job") }')
println cl.class
cl.call()
In addition to Michael Easter's answer, you could also pass the script's binding through to the GroovyShell
def build = { ->
"BUILD $it"
}
def shell = new GroovyShell( this.binding )
def c = shell.evaluate( "{ -> build( 'tim_yates' ) }" )
c()
If you are evaluating the String from your DSL configuration script, you do not need to create a GroovyShell object.
Your script will be run as a subclass of Script which provides a convenience method for evaluating a string with the current binding.
public Object evaluate(String expression)
throws CompilationFailedException
A helper method to allow the dynamic evaluation of groovy expressions using this scripts binding as the variable scope
So in this case, you'd just need to call evaluate('{ -> build("my job") }').
Related
I would like to write a system groovy script which inspects the queued jobs in Jenkins, and extracts the build parameters (and build cause as a bonus) supplied as the job was scheduled. Ideas?
Specifically:
def q = Jenkins.instance.queue
q.items.each { println it.task.name }
retrieves the queued items. I can't for the life of me figure out where the build parameters live.
The closest I am getting is this:
def q = Jenkins.instance.queue
q.items.each {
println("${it.task.name}:")
it.task.properties.each { key, val ->
println(" ${key}=${val}")
}
}
This gets me this:
4.1.next-build-launcher:
com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty$ScannerJobPropertyDescriptor#b299407=com.sonyericsson.jenkins.plugins.bfa.model.ScannerJobProperty#5e04bfd7
com.chikli.hudson.plugin.naginator.NaginatorOptOutProperty$DescriptorImpl#40d04eaa=com.chikli.hudson.plugin.naginator.NaginatorOptOutProperty#16b308db
hudson.model.ParametersDefinitionProperty$DescriptorImpl#b744c43=hudson.mod el.ParametersDefinitionProperty#440a6d81
...
The params property of the queue element itself contains a string with the parameters in a property file format -- key=value with multiple parameters separated by newlines.
def q = Jenkins.instance.queue
q.items.each {
println("${it.task.name}:")
println("Parameters: ${it.params}")
}
yields:
dbacher params:
Parameters:
MyParameter=Hello world
BoolParameter=true
I'm no Groovy expert, but when exploring the Jenkins scripting interface, I've found the following functions to be very helpful:
def showProps(inst, prefix="Properties:") {
println prefix
for (prop in inst.properties) {
def pc = ""
if (prop.value != null) {
pc = prop.value.class
}
println(" $prop.key : $prop.value ($pc)")
}
}
def showMethods(inst, prefix="Methods:") {
println prefix
inst.metaClass.methods.name.unique().each {
println " $it"
}
}
The showProps function reveals that the queue element has another property named causes that you'll need to do some more decoding on:
causes : [hudson.model.Cause$UserIdCause#56af8f1c] (class java.util.Collections$UnmodifiableRandomAccessList)
I was going to use Groovy's inspect() methods for quick and dirty object persistence but it didn't work as I expected. More specifically if I inspect() a list then list items are not inspect()ed but rather are toString()ed instead. Consider the following script:
class Foo {
String inspect() { 'new Foo()' }
}
assert new Foo().inspect() == 'new Foo()' // passes as expected
assert [new Foo()].inspect() == '[new Foo()]' // fails
Running this script produces the following output:
Assertion failed:
assert [new Foo()].inspect() == '[new Foo()]'
| | |
| | false
| [Foo#3d52315f]
Foo#3d52315f
at test.run(test.groovy:6)
meaning that inspect() on my Foo instance is never called. Is this a Groovy bug? I'm testing with Groovy 2.4.1.
Try in the following way:
class Foo {
String inspect() { 'new Foo()' }
}
assert new Foo().inspect() == 'new Foo()' // passes as expected
assert [new Foo()]*.inspect().toString() == '[new Foo()]'
You need to call inspect on every element of the list provided and then make to list inspectable itself. But indeed the behavior seems strange when it comes to the docs.
It seems that this is a bug. From DefaultGroovyMethods call to inspect() is redirected to InvokerHelper. Since you pass a collection (list) formatList method will be invoked. This method iterates over the list passed and calls format in the same class. Since formatdoesn't know about Foo it will call toString to the object passed (line 629).
The following example shows how it works:
class Foo {
String inspect() { 'new Foo()' }
String toString() { 'new Foo()' }
}
assert new Foo().inspect() == 'new Foo()'
assert [new Foo()].inspect() == '[new Foo()]'
I am implementing a pluggable architecture based on http://fbflex.wordpress.com/2010/03/14/hot-pluggable-extensions-in-grails-adding-and-changing-your-application-behaviour-on-the-fly/.
As it turns out, one of my pluggable closures needs to invoke another closure in the same file.
However, during execution, the call from one closure to the other fails with this exception:
No signature of method: groovy.util.ConfigSlurper$_parse_closure5.criterion2() is applicable for argument types: (java.util.LinkedHashMap)
This error is not true. There actually is a closure that takes a map. I think the problem is with the scoping or qualifying of the closure.
name ="strategy1"
key ="strategy1"
criterion1 = { params ->
params.a > params.b
}
criterion2 = { params ->
params.a >= params.c && params.a >= params.d
}
constructWidget = { params ->
def base = [symbol:params.sym, price:params.pr, strategy:params.strat]
if( criterion2(base) ) { // this is where the exception occurs
// ...
}
}
Calls to these closures work fine from outside of the "plugin." What is the proper way to refer to the closure named criterion2 from inside of constructWidget?
The problem you describe is well-reproducible, when loading the plugins with the help of ConfigSlurper:
def plugin = new ConfigSlurper().parse( new File('plugin1.groovy').toURL() )
plugin.constructWidget([:])
the problem is solved by using GroovyShell instead of ConfigSlurper:
Binding binding = new Binding()
GroovyShell shell = new GroovyShell(binding)
def plugin = shell.evaluate(new File('plugin1.groovy'))
plugin.constructWidget([:])
update-20140104:
if you are loading plugins in a loop:
new File( grailsApplication.config.externalFiles ).eachFile { file ->
Binding binding = new Binding()
GroovyShell shell = new GroovyShell(binding)
shell.evaluate(file)
strategies.put( binding.variables.key, binding.variables )
}
File1.groovy
def method() {
println "test"
}
File2.groovy
method()
I want to load/include the functions/methods from File1.groovy during runtime, equals to rubys/rake's load. They are in two different directories.
If you don't mind the code in file2 being in a with block, you can do:
new GroovyShell().parse( new File( 'file1.groovy' ) ).with {
method()
}
Another possible method would be to change file1.groovy to:
class File1 {
def method() {
println "test"
}
}
And then in file2.groovy you can use mixin to add the methods from file1
def script = new GroovyScriptEngine( '.' ).with {
loadScriptByName( 'file1.groovy' )
}
this.metaClass.mixin script
method()
You can evaluate any expression or script in Groovy using the GroovyShell.
File2.groovy
GroovyShell shell = new GroovyShell()
def script = shell.parse(new File('/path/file1.groovy'))
script.method()
It will be easiest if file1.groovy is an actual class class File1 {...}.
Given that, another way to do it is to load the file into the GroovyClassLoader:
this.class.classLoader.parseClass("src/File1.groovy")
File1.method()
File1.newInstance().anotherMethod()
I am late on this but. This is how we have been achieving what you were asking. So, i have a file1.gsh like so:
File1:
println("this is a test script")
def Sometask(param1, param2, param3)
{
retry(3){
try{
///some code that uses the param
}
catch (error){
println("Exception throw, will retry...")
sleep 30
errorHandler.call(error)
}
}
}
return this;
And in the other file, these functions can be accessed by instantiating first. So in file2.
File2:
def somename
somename = load 'path/to/file1.groovy'
//the you can call the function in file1 as
somename.Sometask(param1, param2, param3)
Here is what I'm using.
1: Write any_path_to_the_script.groovy as a class
2: In the calling script, use:
def myClass = this.class.classLoader.parseClass(new File("any_path_to_the_script.groovy"))
myClass.staticMethod()
It's working in the Jenkins Groovy script console. I have not tried non-static methods.
The answer by #tim_yates that uses metaClass.mixin should have worked without needing any changes to file1.groovy (i.e., mixin with the script object), but unfortunately there is a bug in metaClass.mixin that causes a SO error in this scenario (see GROOVY-4214 on this specific issue). However, I worked around the bug using the below selective mixin:
def loadScript(def scriptFile) {
def script = new GroovyShell().parse(new File(scriptFile))
script.metaClass.methods.each {
if (it.declaringClass.getTheClass() == script.class && ! it.name.contains('$') && it.name != 'main' && it.name != 'run') {
this.metaClass."$it.name" = script.&"$it.name"
}
}
}
loadScript('File1.groovy')
method()
The above solution works with no changes being needed to File1.groovy or the callers in File2.groovy (except for the need to introduce a call to loadScript function).
This is a short Groovy script:
import org.apache.commons.io.FileUtils;
def dir = new File("/mydir")
def files = FileUtils.listFiles(dir, new String[] { "java" }, false)
It says:
No expression for the array constructor call at line: 2
What's wrong?
The call should be:
def files = FileUtils.listFiles(dir, [ "java" ] as String[], false)
Groovy uses Lists by default, and the as operator can be used to coerce these lists into arrays of a specified type (often for interacting with the java api as in this example)
[edit]
As an aside, you can do this with pure Groovy like so:
def files = dir.listFiles().findAll { it.name ==~ /.*\.java/ }
Then, you don't need Commons FileUtils