Groovy Help : Using closures correctly

384 views
Skip to first unread message

zealous...@gmail.com

unread,
Mar 3, 2015, 8:30:26 PM3/3/15
to job-dsl...@googlegroups.com


Hi,

I have a syntax issue when trying to use the Job DSL Plugin. The primary issue is my level of knowledge in groovy.


MyApplication1.createDeploymentJob(this, arg1, arg2)
class MyApplication1 {
    static createDeploymentJob(dslFactory, arg1, arg2) {
        dslFactory.job {
            name arg1
            steps {
                ant(arg2)
            }
        }
    }
}

What i am trying to do is to apply some default parameters to this job which is being created.  So, while name, steps, etc. will be defined in this API. I want to add some common properties to this job definition but only if the application has not defined it.

I thought of two conceptual possibilities
 - Pass in a default block to the API which is applied by the application [However, i don't want the application to care about the invidual properties, i just want it to blatantly apply them to the configuration]
 - Apply default parameters to the return of the API.

I need some syntactical help and what is the groovy way of accomplishing this?

Any pointers are appreciated..

Jon Woods

unread,
Mar 5, 2015, 2:15:51 PM3/5/15
to job-dsl...@googlegroups.com
Hi.

I understand how you feel.  I had no Groovy knowledge at the time I first wanted to get into JobDSL, and even now it's fairly cursory.

When you say 'the application', I guess that corresponds to your example class MyApplication1.  To give your application the wherewithal to apply configuration defined outside, you can pass it a closure and have that closure executed if/as appropriate:

class MyApplication1 {
    static createDeploymentJob(dslFactory, arg1, arg2, externalConfiguration) {
        def job = dslFactory.job {
            name arg1
            // etc
        }

        // Maybe do the following only if some condition holds...
        job.with externalConfiguration
    }
}


and at the call site:

MyApplication1.createDeploymentJob(this, "some-name", "bla") {
    description "description provided by call site"
}


The latter snippet uses the fact that when the last parameter of a method is a closure, you can provide its value using the syntax shown above, with the closure outside the argument list brackets.

You could of course make createDeploymentJob return a job, and assign that return value to a variable, then use thatVariable.with - but you said you wanted MyApplication1 to make the decision whether or not to apply config.

You could also use syntax more familiar to Java devs, and maybe define the closure elsewhere:

def commonConfiguration = {
    description "description from closure defined separately near call site"
}


MyApplication1.createDeploymentJob(this, "bla", "bla", commonConfiguration)

You can use the fact that JobDSL scripts are real Groovy in many other ways.  For example, we define our jobs then iterate over a collection of them and apply a common configuration:

[job1, job2, job3].each { it->
    it.with {
        // ...
    }
}


As you refine your JobDSL script, you may find it gets quite hacky, with all sorts of tricks used to contribute the right configuration to the right job.  I'd definitely encourage you to use helper methods to separate out common configuration, but also to look for a more declarative style.  We're moving towards defining our own job metadata - 'requires virtual backends', 'should deploy the app', 'has configuration <from here>' - and then our scripts examine that metadata and apply JobDSL magic as appropriate.

When dealing with a Groovy DSL like JobDSL, I think there's one main concept to get into your head.  Speaking from personal experience, if you can do that before mucking around too much with JobDSL, it will save a fair bit of time and frustration.  The concept is that of closures and their execution contexts, ie the contexts in which symbols are evaluated.  I found these helpful:

http://groovy.codehaus.org/Closures
https://namingcrisis.net/weblog/2009/08/17/groovy-closure-and-its-execution-context/

Even then, in a DSL like JobDSL you can't always tell from the documentation what kind of execution context you'll have been given - see the comment on the first page re delegates and builders - and it also depends on how the DSL script is itself being evaluated.  But at least you'll know more about how to experiment.

And you should experiment with tiny snippets of code, not the full pipeline you're trying to define.  You should also look to unit-testing your job defs, runnable from your IDE and in your pipeline and not just by mucking around at the Jenkins UI.  We rolled our own means of doing this, but I think there's probably support within JobDSL itself nowadays... I must do some research.

Jon

--
You received this message because you are subscribed to the Google Groups "job-dsl-plugin" group.
To unsubscribe from this group and stop receiving emails from it, send an email to job-dsl-plugi...@googlegroups.com.
To post to this group, send email to job-dsl...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/job-dsl-plugin/0d8a5924-c63e-4fb2-8f70-c11a69d78020%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

zealous...@gmail.com

unread,
Mar 9, 2015, 9:36:49 AM3/9/15
to job-dsl...@googlegroups.com

This is very insightful. Thanks.

As i am doing this i am realizing that i am navigating through the same trenches you have described. An interesting ride.

I really appreciate the time you took to post such a detailed response.

Thanks
Reply all
Reply to author
Forward
0 new messages