Dynamically generated closure - groovy

Dynamically generated closure
I've written soap request in groovy wslite :
def request = {
envelopeAttributes('xmlns:art': 'http://url')
body {
'art:validate' {
item(itemValue)
}
}
}
It's working fine, but now I have to change this to list, so at the end it will be something like that:
def request = {
envelopeAttributes('xmlns:art': 'http://url')
body {
'art:validate' {
item(itemValue)
item(itemValue2)
item(itemValue3)
}
}
}
But have know Idea how I can dynamically create this request from List. I've even extracted this to variable:
def items = {
item(itemValue)
item(itemValue2)
item(itemValue3)
}
but I don't know how to add new items to this closure. Is there any easy way ?

Builder closures are normal Groovy code, so something like
def values = [itemValue, itemValue2, itemValue3]
def request = {
envelopeAttributes('xmlns:art': 'http://url')
body {
'art:validate' {
values.each { item(it) }
}
}
}
should work fine. Or if you have
def items = {
item(itemValue)
item(itemValue2)
item(itemValue3)
}
then you can do
def request = {
envelopeAttributes('xmlns:art': 'http://url')
body {
'art:validate'(items)
}
}
(passing the existing closure to art:validate rather than defining a new one inline).

With your given items Closure, this may work:
def request = {
envelopeAttributes('xmlns:art': 'http://url')
body {
'art:validate' {
items.delegate = delegate
items()
}
}
}
if you need other things inside art:validate

Related

Jenkins Library / Groovy Script - Wrap Code dynamically

I'm developing a Jenkins shared library right now.
I wasn't able to figure how to easily "wrap" a code inside a function without copy-pasting the whole code. For example: If a developer sets a value to true, then I want to wrap the whole code inside a function. Right now I want to use this to allow e.g. the gitlabIntegration to be turned off from the Jenkinsfile.
Example:
// vars/stageWrapper.groovy
def call(Map parameters = [:], body) {
stage(stageName) {
if (pushtoGitlab) {
gitlabCommitStatus(stageName) {
if (!containerName) body()
else {
container(containerName) {
body()
}
}
}
} else {
if (!containerName) body()
else {
container(containerName) {
body()
}
}
}
}
}
let the user select if the stage should be pushed to gitlab via the gitlabCommitStatus wrapper.
switch to a specified container or use default container (if none is specified)
To realize this I currently repeat the code, which I really don't like...
Is there any way of achieving the same, but without repeating the same code over and over?
Thank You!
In Groovy you can reuse a Closure in different DSL-Builders by setting it's delegate to builder's delegate.
Something like this should work:
def containerNameBody = { body ->
if (!containerName)
body()
else
container(containerName) {
body()
}
}
def call(Map parameters = [:], body) {
stage(stageName) {
containerNameBody.delegate = delegate
if (pushtoGitlab)
gitlabCommitStatus(stageName) {
containerNameBody body
}
else
containerNameBody body
}
}
How about following approach to pass down the param into function, then decide how to do inside the function by the param value.
def gitHub(gitHubOn) {
}
def gitLab(gitLabOn) {
}
def call(Map parameters = [:], body){
//some code....
foo=bar
gitLab(parameters.gitLabOn)
gitHub(parameters.gitHubOn)
body()
}

I want to acess the value of this json object without using the key name

{
"callExpDateMap": {
"2020-03-06:2": {
"305.0": [
{
"putCall": "CALL,
}
]
}
}
}
So this is my json object.And As you can see in "callExpDateMap",there is a date("2020-03-06:2" and then there is a value("305.0").The date("2020-03-06:2") and price("305.0") values will not be the same in every response.So If I want to access the value of "putCall" (Please remember that I can't use the key of the date and the price because it keeps changing in every response), How can I do this?...I'm using nodejs.
You need to pass this JSON to a function and call that function recursively with each inner object (or value of JSON key) and when value is string just print it or use in your way.
forEach(Object.keys(jsonobj))
function myfunc(key){
if((typeof jsonobj[key])==object){
forEach(Object.keys(jsonobj[key]));
}else{
console.log(jsonobj[key])
}
}
}
You could loop over the object and get the value,
Example:
let test = {
callExpDateMap: {
"2020-03-06:2": {
"305.0": [
{
putCall: "CALL"
}
]
}
}
};
let callExpDateMap = test.callExpDateMap;
for(const dateValue in callExpDateMap) {
for(const put in callExpDateMap[dateValue]) {
console.log(callExpDateMap[dateValue][put][0]['putCall']);
}
}
or you could use Object.keys to get the value. it's simpler then the previous approach;
let obj = {
callExpDateMap: {
"2020-03-06:2": {
"305.0": [
{
putCall: "CALL"
}
]
}
}
};
let callExpDateMap = obj.callExpDateMap;
let dateKey = Object.keys(callExpDateMap);
let price = Object.keys(callExpDateMap[dateKey]);
let putCall = callExpDateMap[dateKey][price];
console.log(putCall);

Can I pass in a method in place of a closure in Groovy?

Let's say I've got something like this:
class Test {
def test_method() {
def http = new HTTPBuilder("http://rest.request.com")
http.request(groovyx.net.http.Method.GET) { req ->
uri.path = "/path/to/rest/request"
response.success = {resp, reader ->
println resp
}
}
}
}
This works fine and all, but I'd really prefer to do something like this:
class Test {
def print_resp(String resp) {
println resp
}
def test_method() {
def http = new HTTPBuilder("http://rest.request.com")
http.request(groovyx.net.http.Method.GET) { req ->
uri.path = "/path/to/rest/request"
response.success = print_resp
}
}
}
Except I've got the syntax wrong there. Please help me understand what I'm doing wrong.
You want to use the .& syntax:
def test_method() {
def http = new HTTPBuilder("http://rest.request.com")
http.request(groovyx.net.http.Method.GET) { req ->
uri.path = "/path/to/rest/request"
response.success = this.&print_resp
}
}

Groovy: Implicit call not working on instance variables inside closure

A class implements call method so that it's objects can be called as a method. This works for most of the case but not when the call is being made inside a closure on a object which is instance variable of a class.
To demonstrate the problem, in the code below I've commented the interesting lines with numbers. While most variants result in same output, only the line with comment 5 doesn't work. It throws groovy.lang.MissingMethodException: No signature of method: Client2.instanceVar() is applicable for argument types: () values: [])
Can someone help me understand the reason? Is it a bug?
class CallableObject {
def call() { println "hello" }
}
class Client {
def instanceVar = new CallableObject()
def method() {
def localVar = new CallableObject()
def closure1 = { localVar() }
def closure2 = { instanceVar.call() }
def closure3 = { instanceVar() } // doesn't work
localVar() // 1
instanceVar() // 2
closure1() // 3
closure2() // 4
closure3() // 5
}
}
new Client().method()
I guess this will make it clear.
class CallableObject {
def call() { println "hello" }
}
class Client {
def instanceVar = new CallableObject()
def getInstanceVar() {
println "Getter Called"
instanceVar
}
def method() {
def localVar = new CallableObject()
def closure1 = { localVar() }
def closure2 = { instanceVar.call() }
def closure3 = { this.#instanceVar() } //should work now
localVar() // 1
instanceVar() // 2
closure1() // 3
closure2() // 4
closure3() // 5
}
}
new Client().method()
You will see "Getter Called" printed when closure2() invoked. For a global property to be accessed in the closure inside a method, the getter in called instead. To surmount the error you get, the field instanceVar needs to be accessed directly in order to implicitly use call().

Reduce code repetition in Groovy closures

In a piece of Gradle build script, the amount of code i'm repeating is increasing. All tasks have a big part in common, except for a few lines:
task copyZipFile() {
doLast {
def remoteBuildProperties = getRemoteBuildProperties(project)
ant {
taskdef(name: 'ftp',
classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP',
classpath: configurations.ftpAntTask.asPath)
ftp(server: remoteBuildProperties['host.name'],
userid: remoteBuildProperties['username'],
password: remoteBuildProperties['password'],
remotedir: 'some-folder', // This value differs from call to call
passive: 'true') {
// The next two lines also are different per case, and might be less or more lines
fileset(dir: rootProject.buildDir) { include(name: 'build.zip') }
fileset(dir: rootProject.projectDir) { include(name: 'build.properties') }
}
}
}
}
I don't like to repeat myself, so I'd like to reduce this code to a new helper method that does this trick, and a simple caller, something like:
task copyZipFile() {
doLast {
def remoteBuildProperties = getRemoteBuildProperties(project)
upload(remoteBuildProperties, 'some-folder') {
fileset(dir: rootProject.buildDir) { include(name: 'build.zip') }
fileset(dir: rootProject.projectDir) { include(name: 'build.properties') }
}
}
}
How would I achieve this?
You can pass the inner closure to your upload method as the final parameter. Set the delegate to the original builder delegate so the inner closure calls get handled properly. For example:
def upload(remoteBuildProperties, folder, body) {
ant {
taskdef(name: 'ftp',
classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP',
classpath: configurations.ftpAntTask.asPath)
ftp(server: remoteBuildProperties['host.name'],
userid: remoteBuildProperties['username'],
password: remoteBuildProperties['password'],
remotedir: folder,
passive: 'true') {
body.delegate = delegate
body()
}
}
}

Resources