Groovy method as variable - groovy

I have the following Groovy script:
def Deploy() {
if (App == "TEST"){
def book = load "book.groovy"
book.buildList.each {
a lot of actions
}
else {
book.each {
the same a lot of actions
}
}
So the difference only in execution methods (properties): book.buildList.each or book.each. How to avoid to repeat those a lot of actions and keep code cleaner. Probably there is a way to put book.buildList.each or book.each into the variable?
File Book.groovy contain few map:
buildList = [
'key1':'value1',
'key2':'value2',
'key3':'value3',
]
anotherList = [
'key11':'value11',
'key22':'value22',
'key33':'value33',
]
return this
But if App not "TEST" I have book map:
[
'key1':'value1',
'key2':'value2',
'key3':'value3',
]

If both each invocations do the exact same thing, you can extract the body of the closure you pass to each method to a variable and share it between both invocations. Consider the following (simplified) example:
def closure = { k, v ->
println "k = $k, v = $v"
}
def a = [
'key1': 'value1',
'key2': 'value2',
'key3': 'value3',
]
def b = [
'key11': 'value11',
'key22': 'value22',
'key33': 'value33',
]
println "Calling common closure on the map a:"
a.each closure
println "Calling common closure on the map b:"
b.each closure
In this example, I common closure to the variable, and then I was able to re-use it in both iterations. Also, keep in mind that in your example you are using maps, not lists. Calling each on map is absolutely correct, and in this case, you can use a closure with k and v parameters to map key and value accurately.

Related

How to pass multiple optional parameters and one mandatory parameter in groovy? [duplicate]

I would like to write a wrapper method for a webservice, the service accepts 2 mandatory and 3 optional parameters.
To have a shorter example, I would like to get the following code working
def myMethod(pParm1='1', pParm2='2') {
println "${pParm1}${pParm2}"
}
myMethod();
myMethod('a')
myMethod(pParm2:'a') // doesn't work as expected
myMethod('b','c')
The output is:
12
a2
[pParm2:a]2
a2
bc
What I would like to achieve is to give one parameter and get 1a as the result.
Is this possible (in the laziest way)?
Can't be done as it stands... The code
def myMethod(pParm1='1', pParm2='2'){
println "${pParm1}${pParm2}"
}
Basically makes groovy create the following methods:
Object myMethod( pParm1, pParm2 ) {
println "$pParm1$pParm2"
}
Object myMethod( pParm1 ) {
this.myMethod( pParm1, '2' )
}
Object myMethod() {
this.myMethod( '1', '2' )
}
One alternative would be to have an optional Map as the first param:
def myMethod( Map map = [:], String mandatory1, String mandatory2 ){
println "${mandatory1} ${mandatory2} ${map.parm1 ?: '1'} ${map.parm2 ?: '2'}"
}
myMethod( 'a', 'b' ) // prints 'a b 1 2'
myMethod( 'a', 'b', parm1:'value' ) // prints 'a b value 2'
myMethod( 'a', 'b', parm2:'2nd') // prints 'a b 1 2nd'
Obviously, documenting this so other people know what goes in the magical map and what the defaults are is left to the reader ;-)
You can use arguments with default values.
def someMethod(def mandatory,def optional=null){}
if argument "optional" not exist, it turns to "null".
Just a simplification of the Tim's answer. The groovy way to do it is using a map, as already suggested, but then let's put the mandatory parameters also in the map. This will look like this:
def someMethod(def args) {
println "MANDATORY1=${args.mandatory1}"
println "MANDATORY2=${args.mandatory2}"
println "OPTIONAL1=${args?.optional1}"
println "OPTIONAL2=${args?.optional2}"
}
someMethod mandatory1:1, mandatory2:2, optional1:3
with the output:
MANDATORY1=1
MANDATORY2=2
OPTIONAL1=3
OPTIONAL2=null
This looks nicer and the advantage of this is that you can change the order of the parameters as you like.
We can Deal with Optional parameters in 2 ways
Creating the method parameter with null values:
def generateReview(def id, def createDate=null) {
return new Review(id, createDate ?: new Date()) // ?: short hand of ternary operator
}
generateReview(id) // createDate is not passed
generateReview(id, createDate) // createDate is passed
Using Java Optional.of()
def generateReview(def id, Optional<Date> createDate) {
return new Review(id, createDate.isPresent() ? createDate.get() : new Date())
}
generateReview(id, Optional.empty()) // createDate is not passed
generateReview(id, Optional.of(createDate)) // createDate is passed

Groovy: Usage of 'it' with closures

I wonder how I can pass the implicit variable 'it' to the closure.
See the following code:
def myfunc(String name, Closure cl)
{
println("name: ${name}")
cl.call()
}
list = ['a', 'b']
list.each
{
println("it1: ${it}")
}
list.each
{
myfunc("f1")
{
println("it2: ${it}")
}
}
list.each
{
p ->
myfunc("f2")
{
println("p: ${p}")
}
}
The resulting output is:
it1: a
it1: b
name: f1
it2: null
name: f1
it2: null
name: f2
p: a
name: f2
p: b
How can I achieve that in the second version (f1/it2) the implicit variable it is passed to the closure? Currently it is "null".
I want to achieve to have 'a', 'b' within 'it' in the second version.
The background of the question is, that within a Jenkins Pipeline, the following code
mylist = ['a1', 'a2']
mylist.each {
dir ("xxxx") {
echo "it in xxxx is ${it}"
}
}
prints out the values 'a1' and 'a2' for it.
This does not fit to the understanding that it is bound to the outermost closure.
In case of Jenkins the behaviour seems to be different.
I want to achieve the very same behaviour and I am wondering how to do this.
Thanks for all feedback!
Best regards
Mathias
dir from jenkins is not a function - it's a jenkins Step and your groovy function example is not applicable.
you have to check if it's achievable from groovy-defined custom steps https://rubix.nl/jenkins-creating-a-custom-pipeline-step-in-your-library/
or think about custom plugin creation.
btw, here is a source of dir jenkins step: https://github.com/jenkinsci/workflow-basic-steps-plugin/blob/master/src/main/java/org/jenkinsci/plugins/workflow/steps/PushdStep.java

Call stage from function in Jenkinsfile

I have in my Jenkinsfile:
def foo = ["1", "2", "3"]
def parallelStagesFromMap = foo.collectEntries {
["Build ${it}" : generateStage(it)]
}
def generateStage(bar) {
return {
stage("Build ${bar}") {
echo "Building for ${bar}"
}
}
}
I can then use them with parallel parallel parallelStagesFromMap but now I'm trying to call one in particular, for example:
generateStage("a") and it is just skipped... Am I missing anything?
You are missing closure invocation. Your generateStage(name) method returns a closure, and this closure is not invoked implicitly. (It works with parallel stages, because parallel method expects a map where each entry value is a closure, so it iterates over all map entries and invokes collected closures).
Here is what your example should look like to add a non-parallel stage to the pipeline using generateStage(name) method:
def foo = ["1", "2", "3"]
def parallelStagesFromMap = foo.collectEntries {
["Build ${it}" : generateStage(it)]
}
def generateStage(bar) {
return {
stage("Build ${bar}") {
echo "Building for ${bar}"
}
}
}
node {
parallel parallelStagesFromMap
generateStage("skipped") // no invocation, stage is skipped
generateStage("nonparallel").call()
}
And here is what the Blue Ocean UI looks like after running this exemplary pipeline:

Inject String to Map - Groovy

I am new to Groovy and working on a device handler for my Smartthing Hub which is written in Groovy. I am having trouble parsing a string.
def parseDescriptionAsMap(description) {
println "description: '${description}"
def test = description.split(",")
println "test: '${test}"
test.inject([:]) { map, param ->
def nameAndValue = param.split(":")
println "nameAndValue: ${nameAndValue}"
if(map)
{
println "map is NOT NULL"
map.put(nameAndValue[0].trim(),nameAndValue[1].trim())
}
else
{
println "map is NULL!"
}
}
}
Output:
description: 'index:17, mac:AAA, ip:BBB, port:0058, requestId:ce6598b2-fe8b-463d-bdf3-01ec35055f7a, tempImageKey:ba416127-14e3-4c7b-8f1f-5b4d633102e5
test: '[index:17, mac:AAA, ip:BBB, port:0058, requestId:ce6598b2-fe8b-463d-bdf3-01ec35055f7a, tempImageKey:ba416127-14e3-4c7b-8f1f-5b4d633102e5]
nameAndValue: [index, 17]
nameAndValue: [ mac, AAA]
map is NULL!
nameAndValue: [ ip, BBB]
map is NULL!
map is NULL!
nameAndValue: [ port, 0058]
nameAndValue: [ requestId, ce6598b2-fe8b-463d-bdf3-01ec35055f7a]
Two questions:
1. Why is the variable, map, null?
2. Why is the function not printing the nameAndValue->'tempImageKey' info?
map cannot be null, if(map) checks if it's null or empty...and it will not be null in this situation (so long as you follow #2)
You need to return map from the inject closure, so that is can be aggregated.
test.inject([:]) { map, param ->
def nameAndValue = param.split(":")
println "nameAndValue: ${nameAndValue}"
map.put(nameAndValue[0].trim(),nameAndValue[1].trim())
map
}
A simpler version of what you're trying would be:
description.split(',')*.trim()*.tokenize(':').collectEntries()

ConfigSlurper bad result with nested values with same key

I'm trying to use the Groovy's ConfigSlurper in a projetct but it happens to have a weird behaviour when the same key appears two times in the configuration. Here is an example :
def struct = """
node2 {
subnode21="Test1"
}
root1 {
node2 {
subnode1="Test2"
subnode2=node2.subnode21
}
}
"""
def config = new ConfigSlurper().parse(struct)
This will produce the following result :
[
node2:[
subnode21:Test1
],
root1:[
node2:[
subnode1:Test2,
subnode21:[:],
subnode2:[:]
]
]
]
ConfigSlurper seems to consider that subnode2=node2.subnode21 in the root1 closure refers to root1.node2 and not the node2 closure declared above.
Is this a known limitation or am I missing something ?

Resources