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 )
}
Related
I'm trying to create a config file for my java app that can have lazy evaluation, for instance, contain an e-mail template with current date time. I use ConfigSlurper to parse the config file.
For some reason I can't find at https://groovy-lang.org/closures.html the description of the commonly used form (see build.gradle): prop1 { ... } prop2 { ... }. Am I looking at the wrong place?
My tests show that such closures are evaluated immediately and the example above can be rewritten as: prop1 = { ... }() prop2 = { ... }() (notice the parentheses, without them the evaluation is lazy).
I also don't understand why using dots in property without the equal sign cause MissingMethodException: No signature of method: groovy.util.ConfigObject.URL() if I use: server.URL { System.out.println('ccc'); 'asd' } in the example below.
It's disappointing that the choice is completely transparent for Groovy, but you have to check that a value is instance of Closure or GString when using java.
I used this article as the starting point https://blog.mrhaki.com/2009/08/grassroots-groovy-configuration-with.html
// We can group settings.
// The following settings are created:
// app.version, app.setting1, app.setting2, app.setting3, app.date, app.active
app {
System.out.println('aaa')
version = "1.0"
// We can write code to set up settings.
[1, 2, 3].each {
this."setting${it}" = it * 10
}
// We can use Java objects
date = new Date()
active = true
}
server.URL = "http://default"
// Environment specific settings override settings with the
// same name.
environments {
development {
server = { System.out.println('ccc'); 'asd' }
// server.URL = "${-> System.out.println('bbb');new Date()}http://localhost"
}
test {
server.URL = 'http://test:9080'
}
integrationtest {
server.URL = 'http://integrationtest/url'
}
production {
server.URL = 'http://prod/url'
}
}
My tests show that such closures are evaluated immediately and the
example above can be rewritten as: prop1 = { ... }()
Closures are not evaluated immediately. The parens above are causing the closure to be evaluated.
For some reason I can't find at https://groovy-lang.org/closures.html
the description of the commonly used form (see build.gradle): prop1 {
... } prop2 { ... }.
prop1 {} is invoking a method named prop1 and passing a closure as a parameter That code is equivalent to this:
def myClosure = {}
prop1(myClosure)
I also don't understand why using dots in property without the equal
sign cause MissingMethodException: No signature of method:
groovy.util.ConfigObject.URL()
Something like server.URL = 'http://test:9080' is assigning a value (http://test:9080) to a property (server.URL). If you remove the equal sign you are changing the expression fundamentally and then would be invoking a method named URL on an object named server and passing http://test:9080 as a parameter.
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 am trying to write a generic program in Groovy that will get the SQL from config file along with other parameters and put them into file.
here is the program:
def config = new ConfigSlurper().parse(new File("config.properties").toURL())
Sql sql = Sql.newInstance(config.db.url, config.db.login, config.db.password, config.db.driver);
def fileToWrite = new File(config.copy.location)
def writer = fileToWrite.newWriter()
writer.write(config.file.headers)
sql.eachRow(config.sql){ res->
writer.write(config.file.rows)
}
in the config the sql is something like this:
sql="select * from mydb"
and
file.rows="${res.column1}|${res.column2}|${res.column3}\n"
when I run it I get
[:]|[:]|[:]
[:]|[:]|[:]
[:]|[:]|[:]
in the file. If I substitute
writer.write(config.file.rows)
to
writer.write("${res.column1}|${res.column2}|${res.column3}\n")
it outputs the actual results. What do I need to do different to get the results?
You accomplish this by using lazy evaluation of the Gstring combined with altering the delegate.
First make the Gstring lazy by making the values be the results of calling Closures:
file.rows="${->res.column1}|${->res.column2}|${-> res.column3}"
Then prior to evaluating alter the delegate of the closures:
config.file.rows.values.each {
if (Closure.class.isAssignableFrom(it.getClass())) {
it.resolveStrategy = Closure.DELEGATE_FIRST
it.delegate = this
}
}
The delegate must have the variable res in scope. Here is a full working example:
class Test {
Map res
void run() {
String configText = '''file.rows="${->res.column1}|${->res.column2}|${-> res.column3}"
sql="select * from mydb"'''
def slurper = new ConfigSlurper()
def config = slurper.parse(configText)
config.file.rows.values.each {
if (Closure.class.isAssignableFrom(it.getClass())) {
it.resolveStrategy = Closure.DELEGATE_FIRST
it.delegate = this
}
}
def results = [
[column1: 1, column2: 2, column3: 3],
[column1: 4, column2: 5, column3: 6],
]
results.each {
res = it
println config.file.rows.toString()
}
}
}
new Test().run()
The good news is that the ConfigSlurper is more than capable of doing the GString variable substitution for you as intended. The bad news is that it does this substitution when it calls the parse() method, way up above, long before you have a res variable to substitute into the parser. The other bad news is that if the variables being substituted are not defined in the config file itself, then you have to supply them to the slurper in advance, via the binding property.
So, to get the effect you want you have to parse the properties through each pass of eachRow. Does that mean you have to create a new ConfigSlurper re-read the file once for every row? No. You will have to create a new ConfigObject for each pass, but you can reuse the ConfigSlurper and the file text, as follows:
def slurper = new ConfigSlurper();
def configText = new File("scripts/config.properties").text
def config = slurper.parse(configText)
Sql sql = Sql.newInstance(config.db.url, config.db.login, config.db.password, config.db.driver);
def fileToWrite = new File(config.copy.location)
def writer = fileToWrite.newWriter()
writer.write(config.file.headers)
sql.eachRow(config.sql){ result ->
slurper.binding = [res:result]
def reconfig = slurper.parse(configText)
print(reconfig.file.rows)
}
Please notice that I changed the name of the Closure parameter from res to result. I did this to emphasize that the slurper was drawing the name res from the binding map key, not from the closure parameter name.
If you want to reduce wasted "reparsing" time and effort, you could separate the file.rows property into its own separate file. i would still read in that file text once and reuse the text in the "per row" parsing.
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") }').
A bit new to groovy, I am trying to match a variable string to a property pulled from a file using ConfigSlurper. I have the slurper part working fine, but can't seem to figure out the right way to evaluate a property with a variable in it. I think I was getting warm when I found evaluating-code-dynamically-in-groovy but I am not entirely sure.
//properties.groovy
jobs {
foo {
email="foo#email.com"
}
}
//myscript.groovy
def config = new ConfigSlurper().parse(new File('properties.groovy').toURI().toURL())
List jobs = (ArrayList) BazAPI.getArtifacts(bucket) // list of objects, foo is one
ListIterator jobIterator = jobs.listIterator();
while (jobIterator.hasNext()) {
Object j = jobIterator.next();
job_name = "${j.name}" //
email = config.jobs."${job_name}".email /* NEED TO FIGURE OUT HOW TO EVAL */
foo_email = config.jobs.foo.email //evaluates to the correct property in properties.groovy
//these values get fed to a DSL but to illustrate
println "${job_name}" // prints foo
println "${email}" // prints [:]
println "${foo_email}" // prints foo#email.com
}
Have you tried
config.jobs[ j.name ].email