Adam Burley
Adam Burley

Reputation: 6069

What does the `varName {` syntax mean in Groovy?

I am totally new to Groovy, coming from a Java background. I am trying to learn it because it's used by the "Job DSL Plugin" in Jenkins. I'm trying to understand the example code snippet posted on the plugin's homepage as a first step.

Here's a snippet of their code for reference:

branches.each {
    def branchName = it.name
    job {
        name "${project}-${branchName}".replaceAll('/','-')
        scm {
            git("git://github.com/${project}.git", branchName)
        }
        steps {
            maven("test -Dproject.name=${project}/${branchName}")
        }
    }
}

What I'm trying to understand is what job { means above. I know it's going to be a Groovy Closure but I don't understand the syntax. All the Groovy tutorials I have read have used a syntax like def job = {...} (defining a closure) or job(...) (calling a closure). Is this a shorthand for one of those? How about the other instances like scm { and steps { - are they working in a similar way?

Upvotes: 1

Views: 200

Answers (2)

tylerwal
tylerwal

Reputation: 1870

Like @tim_yates mentioned, the low-level syntax is a closure but at a higher level this fwould be considered a builder - you can find good examples in MarkupBuilder and JsonBuilder.

I don't know anything about the plugin you are using and whether the job, scm, steps, etc. are predefined, because with a builder you can have predefined method names or dynamic method names with the help of the methodMissing method.

Just a guess, but possibly within the plugin's internal implementation, there is a method named job that instantiates an object and returns it based on the passed in closure.

BranchBuilder {
    Branch branch

    Branch job(Closure passedInClosure) {
        branch = new Branch()
        passedInClosure()
        return branch
    }

    ...
}

This passedInClosure would contain name, scm, steps closures which depending on the implementation would probably exist in the BranchBuilder:

void name(String branchName) {
    branch.name = branchName
}

void scm(Closure passedInClosure) {
    // the passedInClosure at this point contains the git closure which maybe isn't explicity defined
    passedInClosure()
}

To give an example of how the methodMissing could work in this situation (also in BranchBuilder):

def methodMissing(String name, arguments) {
    if(name == 'git') {
        branch.scms << new SourceControl(system: 'Git', url: arguments[0], branchName: arguments[1])
    } else if (name == 'svn') {
        branch.scms << new SourceControl(system: 'Subversion', url: arguments[0], branchName: arguments[1])
    }
}

The Groovy Goodness site does a better job than I can in explaining this but hopefully I've broken it down somewhat.

Upvotes: 1

tim_yates
tim_yates

Reputation: 171054

if you define a method like so:

def job(Closure c) {
    println c()
}

Then in Groovy, you can call:

job({
    "Hello World"
})

And Groovy's parser lets you omit the parentheses, so that can just become:

job {
    "Hello World"
}

Another form you may see follows the rule that if a CLosure is the last parameter of a method, it can be called outside of the parentheses, ie:

def job(String who, Closure c) {
    println c(who)
}

job("World") { who ->
    "Hello $who"
}

Upvotes: 4

Related Questions