1

Trying to learn groovy closures, before writing jenkins pipeline as script.

Below code:

def scores = [72,29,32,44,56]

def analyse(closure){
    closure(scores)
}

def firstScore(array){
    return array[0]
}

analyse(firstScore)

gives error:

groovy.lang.MissingPropertyException: No such property: firstScore for class: groovy.lang.Binding
    at groovy.lang.Binding.getVariable(Binding.java:63)

How to resolve this error?

overexchange
  • 15,768
  • 30
  • 152
  • 347

1 Answers1

1

You see this error because firstScore is a method and not a closure in your code example. You can either change firstScore definition from method to closure, e.g.

def firstScore = { array ->
    return array[0]
}

or you can use Groovy's method pointer operator that converts a method to a closure. In this case you would have to invoke analyze method in the following way:

analyze(this.&firstScore)

Other than that - your Groovy script will still fail. You try to access scores inside the analyze method. You need to know that any method defined in a script gets automatically promoted to be a class level method (every Groovy script compiles to a class that extends groovy.lang.Script class). All other expressions and statements you define in the Groovy script body are a part of the Script.run() method and they are in the local scope. So when the method Script.analyze() gets invoked, it will complain about non-existing property scores, because scores is in the local scope of Script.run() method. To fix it you can annotate scores variable with the @groovy.transform.Field annotation that converts local variable to a class level property - in this case scores can be accessed from any method.

Below you can find an example of the curated script:

import groovy.transform.Field

@Field
def scores = [72,29,32,44,56]

def analyse(closure){
    closure(scores)
}

def firstScore(array){
    return array[0]
}

println analyse(this.&firstScore)

Output:

72

And last but not least. Read "Best Practices for Scalable Pipeline Code" blog post carefully. It explains best practices in writing Jenkins pipeline code. Also, you need to be aware of the fact, that pipeline code gets executed in Groovy CPS mode, which has a bunch of limitations. Knowing them will help you solve problems you will definitely face after jumping from Groovy to a pipeline code.

Szymon Stepniak
  • 40,216
  • 10
  • 104
  • 131
  • Can't we pass methods as arguments? – overexchange Feb 18 '19 at 17:38
  • 1
    Nope, Groovy does not allow you to pass a method/function as a parameter. However, you can use closures for that, and you can transform any method to a closure using method pointer operator I explained in the answer. When you invoke `closure(scores)` in your code, you implicitly invoke `closure.call(scores)`, and `call(args)` is a method that exists in `Closure`. – Szymon Stepniak Feb 18 '19 at 22:56
  • Related question - https://stackoverflow.com/questions/54788484/why-method-cannot-get-into-the-scope-of-node – overexchange Feb 20 '19 at 14:25