I am new to groovy and here is my question
def config = new ConfigSlurper().
parse(new File('RegionConfig.groovy').toURI().toURL())
Now i need something like
for(String name : listOfNames){
println(config.name)
}
How can i achieve this ?
Like this?
config.groovy:
user.name='koji'
user.nation='japan'
a.b.c='foo'
test code:
def config = new ConfigSlurper().parse(new File('config.groovy').toURL())
assert ['user.name', 'user.nation', 'a.b.c'] == config.flatten().keySet().collect {it as String}
also you can write like follows:
for (String name: config.flatten().keySet()) {
println name
}
Related
I'm trying to access a variable in a nextflow.config file during the execution of a pipeline. I want to supply image_standard as a string in run.nf and I want to receive eu.gcr.io/proj_name/image1:latest as an output. I figured out a way to obtain the content of the .config file within the nextflow script, but I don't know how to access this specific property.
This is my nextflow.config file:
process {
withLabel: image_standard {
container = "eu.gcr.io/proj_name/image1:latest"
}
withLabel: image_deluxe {
container = "eu.gcr.io/proj_name/image2:latest"
}
}
the run.nf
x = workflow.configFiles[0]
Properties properties = new Properties()
File propertiesFile = new File("${x}")
propertiesFile.withInputStream {
properties.load(it)
}
log.info "${properties.process}"
Which just prints the line:
{
You could try instead slurping the config file and selecting the process you want using the ProcessConfig class and the applyConfigSelector() method:
import nextflow.config.ConfigParser
import nextflow.script.ProcessConfig
def config_file = file("${baseDir}/nextflow.config")
def config = new ConfigParser().setIgnoreIncludes(true).parse(config_file.text)
def process = new ProcessConfig([:])
process.applyConfigSelector(config.process, 'withLabel:', 'image_standard')
println(process.container)
if i change the code in Groovy DSL Doc here.
add some string 'hello world' to email, like this
email('hello world') { // change here
from 'dsl-guru#mycompany.com'
to 'john.doe#waitaminute.com'
subject 'The pope has resigned!'
body {
p 'Really, the pope has resigned!'
}
}
and change
def email(def name, #DelegatesTo(EmailSpec) Closure cl) { // change here
def email = new EmailSpec()
def code = cl.rehydrate(email, this, this)
code.resolveStrategy = Closure.DELEGATE_ONLY
code.call(name) // change here
}
so, how to modify the class EmailSpec to get the string 'hello world' ??
To tell the compile that the closure will be called with a parameter you need to add the ClosureParams annotation.
To stick with your example:
def email(def name,
#ClosureParams(value = SimpleType, options = "java.lang.String")
#DelegatesTo(EmailSpec) Closure cl) {
def email = new EmailSpec()
def code = cl.rehydrate(email, this, this)
code.resolveStrategy = Closure.DELEGATE_ONLY
code.call(name) // change here
}
will tell the compiler that the first parameter is a String.
For more details have a look at the section The #ClosureParams annotation in the groovy documentation.
Yes, i found a way, but not perfect.
Simple
new EmailSpec(name) // change to
however, i really want to use groovy function call(name) to solve it
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.
Using Groovy 2.0.7, when I have a config.groovy such as:-
def configText = """
switch(environment) {
case 'localhost':
PROXY {
HOST = "localproxy"
}
break
}
PROXY {
HOST = "defaultproxy"
}"""
def config = new ConfigSlurper("localhost").parse(configText)
, I get an assertion failure when I do this:-
assert "localproxy" == config.PROXY.HOST
If I remove the "defaultproxy" line then the environment value is correctly returned.
Am I doing something wrong? This to me is a standard requirement, to have a default value specified for config.PROXY.HOST but be able to override it in the environments switch block.
I know I can use the environments constructor to override the values but that is no use to me as it doesn't allow me to evaluate values, e.g. if I had:-
PROXY {
HOST = "defaultproxy"
URL = "http://" + HOST
}
then the URL would always be http://defaultproxy even if I specified the "localhost" environment.
I need the features from both really! Anyone know how I can achieve this?
You could also put the switch statement after the default proxy.host property...
def configText = """
PROXY {
HOST = "defaultproxy"
}
switch(environment) {
case 'localhost':
PROXY {
HOST = "localproxy"
}
break
}
"""
def config = new ConfigSlurper("localhost").parse(configText)
assert "localproxy" == config.PROXY.HOST
or you could leverage the environment property like this:
def configText = """
PROXY {
HOST = "defaultproxy"
}
environments{
localhost{
PROXY.HOST='localproxy'
}
}
"""
def config = new ConfigSlurper("localhost").parse(configText)
assert "localproxy" == config.PROXY.HOST
OK, I have a solution which works.
I wrapped the switch statement into a function, then called the function at the top of the script (below the function, above the nested scoped properties) and then called the function again at the bottom.
Crude, horrible, but works. If anyone has a better solution, please let me know!
I am building an ant script with groovy markupbuilder. Unfortunately markupbuilder doesn't allow me to create nodes with name 'target' (no problem with targetee),
becauase it throws me
Caught: groovy.lang.MissingMethodException: No signature of method: java.lang.String.call() is applicable for argument types: (java.util.LinkedHashMap, BuildGen$_main_closure1_closure5) values: [[name:clean], BuildGen$_main_closure1_closure5#18efaea]
Possible solutions: wait(), any(), trim(), split(), dump(), next()
so inside my markupbuilder this snippet works:
targete(name: 'clean') {
delete(dir: rootProj.compilerOutput)
}
but I would like to achieve the same with a 'target' node..
I managed to create an empty 'target' node this way:
builder.invokeMethod('target', [name: 'clean'])
but how can I go on and put more nodes inside this 'target' node?
Example of working code:
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder(writer)
builder.project(name: projectName, basedir:'.') {
// works with 'target2' but not with 'target'
'target2'(name: 'build-subprojects') {
rootProj.getAllDependentProjects().each { p->
echo(message: "Compiling project: ${p.projectName}")
// some real stuff
}
}
If I guess right, your problem is you want to create nodes with names that are Groovy keywords?
If so, then you can simply put the name in quotes, like so:
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder( writer )
builder.project {
'for'(name: 'clean') {
delete(dir: '.')
}
}
println writer
That snippet will print out:
<project>
<for name='clean'>
<delete dir='.' />
</for>
</project>
For me, this works:
def projects = [ 'pro_one', 'pro_two' ]
def writer = new StringWriter()
def builder = new groovy.xml.MarkupBuilder(writer)
builder.project( name: 'test', basedir:'.' ) {
'target'( name: 'build-subprojects' ) {
projects.each { p ->
echo( message: "Compiling project: ${p}" )
}
}
}
println writer.toString()
Have you got target set to anything in your code before calling this?
You could try:
builder.target( name: 'build-subprojects' ) {
That might work better?
I've tried Groovy 1.7.5, and 1.8 beta 2 and can't get it to fail :-/