Code works in shell but not as a script - MissingPropertyException - groovy

I'm learning Groovy and came across this curious behavior. Executing the following code in groovysh works without a problem, but saving it as 'foo.groovy' and running it via groovy foo.groovy results in a MissingPropertyException: No such property: a for class: foo in line 3:
def a(n) { n*n }
def x(m) { m(2) }
x(a)
Changing a to a closure a = { n -> n*n } makes this error go away, but I'd like to avoid that for my real use case which is more complex. I've also found this question which deals with the same problem inside of a class, but the solution using this.& isn't applicable as my method isn't inside of a class, and coming from a python background I'd like to keep it that way.
What am I doing wrong? And where is this behavior documented?

It looks like groovysh stores:
def a(n) { n*n }
as a MethodClosure into a binding variable called a
This will not happen outside of groovysh, and as you have seen you'll need to define a as a closure:
def a = { n -> n*n }
def x( m ) { m( 2 ) }
x(a)
Or use the method reference operator &:
def a(n) { n*n }
def x(m) { m(2) }
x( this.&a )

Related

groovy evaluate string as the function which exists in the same script

I am trying to evaluate string as code in groovy and it is failing with groovy.lang.MissingMethodException exception even though method exists in the same script. As I understood groovy runs new instance every time it tries to evaluate the code, but is there any way to inject current script into Eval.me or GroovyShell().evaluate() so that it can find the method and runs it ?
Below is sample code snippet,
def justSayHello(){
return "hello"
}
def my_str = "justSayHello()"
//Eval.me(my_func_str)
new GroovyShell().evaluate(my_func_str)
Both Eval and GroovyShell().evaluate() are throwing below exception
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.justSayHello() is applicable for argument types: () values: []
groovy.lang.MissingMethodException: No signature of method: Script1.justSayHello() is applicable for argument types: () values: []
at Script1.run(Script1.groovy:1)
at string_split.run(string_split.groovy:35)
The following code:
justSayHello = {
println "hello"
}
def my_str = "justSayHello()"
new GroovyShell(binding).evaluate(my_str)
prints out hello when run. Here we have changed justSayHello from a method (on an implicit class that you can not see but which the groovy compiler generates around your script) to a closure. Further we are not doing def justSayHello as that would be defining it as a field on the implicit surrounding class (again which you can't see, but it's there), but rather just defining the variable without any modifiers which puts it in the global binding of the script.
We then send in the binding to the GroovyShell so that it can find the variable.
Result:
─➤ groovy solution.groovy
hello
A more generic variant is to do something like this:
def justSayHello() {
println "hello"
}
def someOtherMethod() {
println "hello again"
}
def methods = this.class.declaredMethods.findResults { m ->
if (m.name.startsWith('$') || m.name in ['main', 'run']) return null
[m.name, this.&"${m.name}"]
}.collectEntries { it }
// just for debugging, print the methods
methods.each { k, v ->
println "method: $k"
}
def my_str = "justSayHello()"
new GroovyShell(new Binding(methods)).evaluate(my_str)
which prints:
─➤ groovy solution.groovy
method: justSayHello
method: someOtherMethod
hello
here we find all the declared methods in the implicit class generated by groovy, remove some stuff added by the groovy compiler (namely main, run and methods starting with a $) and then send the resulting map as the binding for the GroovyShell constructor.
I suspect there might be a more elegant way of accomplishing this, so any groovy gurus - feel free to correct me here.
For an explanation of the implicit enclosing class for a groovy script, see for example this stackoverflow answer.

Groovy collect returning GString in a Jenkins Workflow script

It seems that in the following piece of code:
def formattedPaths = affectedFiles.collect {
"${it.editType.name} ${it.path}"
}
at least sometimes formattedPaths evaluates to a GString instead of a List. This piece of code is a fragment of a larger Jenkins Workflow script, something like:
node {
currentBuild.rawBuild.changeSets[0].collect {
"""<b>${it.user}</b> # rev. ${it.revision}: ${it.msg}
${affectedFilesLog(it.affectedFiles)}"""
}
}
def affectedFilesLog(affectedFiles) {
println "Affected files [${affectedFiles.class}]: $affectedFiles"
def formattedPaths = affectedFiles.collect {
"${it.editType.name} ${it.path}"
}
println "formattedPaths [${formattedPaths.class}]: $formattedPaths"
formatItemList(formattedPaths)
}
def formatItemList(list) {
if (list) {
return list.join('\n')
}
return '(none)'
}
Running this script in Jenkins produces output:
Running: Print Message
Affected files [class java.util.ArrayList]: [hudson.scm.SubversionChangeLogSet$Path#5030a7d8]
Running: Print Message
formattedPaths [class org.codehaus.groovy.runtime.GStringImpl]: edit my/path/flow.groovy
(...)
groovy.lang.MissingMethodException: No signature of method: java.lang.String.join() is applicable for argument types: (java.lang.String) values: [
]
And this makes me believe that in the code:
println "Affected files [${affectedFiles.class}]: $affectedFiles"
def formattedPaths = affectedFiles.collect {
"${it.editType.name} ${it.path}"
}
println "formattedPaths [${formattedPaths.class}]: $formattedPaths"
affectedFiles is ArrayList (script output Affected files [class java.util.ArrayList]: [hudson.scm.SubversionChangeLogSet$Path#5030a7d8] in the output)
but result of running collect method on it - assigned to formattedPaths - is a GString (output: formattedPaths [class org.codehaus.groovy.runtime.GStringImpl]: edit my/path/flow.groovy)
Shouldn't the collect method always return a List?
After the discussion in the comments pointing that it may be some side effects done by the Jenkins Workflow plugin, I decided to use a plain for-each loop:
def affectedFilesLog(affectedFiles) {
println "Affected files [${affectedFiles.class}]: $affectedFiles"
def ret = ""
for (Object affectedFile : affectedFiles) {
ret += affectedFile.path + '\n'
}
println("affectedFilesLog ret [${ret.class}]: $ret")
if (!ret) {
return '(brak)'
}
return ret;
}
EDIT 19/11/2015:
Jenkins workflow plugin mishandles functions taking Closures, see https://issues.jenkins-ci.org/browse/JENKINS-26481 and its duplicates. So rewriting the code to a plain Java for-each loop was the best solution.
You cannot currently use the collect method. JENKINS-26481
I guess your code is not thread safe. If you pass some objects as paramters to some other functions, do not change this. Always create and return a new changed object. Do not manipulate the original data. You should check where your objects live. Is it just inside a function or in global scope?

Properties in Groovy base scripts

I have a DSL where, if present, a closure called before will be called before every command.
In my setup I have 3 files: The script itself - Script, a ScriptBase, that is 'attached' to the script via a CompilerConfiguration, and a Handler.
In the script I may or may not have a closure called before.
before = {
//Do stuff.
}
Notice the lack of a type declaration, or def. If I understand Groovy correctly, this means that before is a in the binding, and accessible from outside code when evaluated with GroovyShell.evaluate().
In the ScriptBase I do the following:
class ProductSpecificationBase extends Script {
def before = null
}
This script base may or may not be overridden later on.
Then, in the Handler, I'm doing a check for whether a before closure is defined in the script:
def config = new CompilerConfiguration()
config.setScriptBaseClass(ScriptBase.class.name)
def shell = GroovyShell()
evaluatedScript = shell.evaluate(new File(thePathToScript))
if (evaluatedScript.before) {
theEvaluationOfMyScript.before()
}
The code works as expected if the script does contain a before closure, but if it doesn't it returns a MissingPropertyException. I've had a look at what this means, and it seems that my before in the ScriptBase isn't considered a property, and all the examples of using these ScriptBases I've found on the internet give examples of using methods. This is not feasible for my use case I'm afraid. How can I ensure that the closure in the ScriptBase is considered a property instead of a field(as I am assuming it is now).
To be paraphrase: I would like my code to not execute the if block if the script does not contain a before closure as well as not having been overridden in an extension of the ScriptBase. However, I would like the evaluation of evaluatedScript.before to be false as it is an empty/null Closure (i.e. it went all the way up to ScriptBase, and found the null closure)
I like to avoid a try/catch approach if possible.
in your example you would basically call the getter for the before property. To check, if there is a method with the name (and params) check with respondsTo. To see, if there is a property at all with that name use hasProperty (Thanks #dmahapatro for pointing this out)
class X {
void before() { println 'x' }
}
class Y { }
class Z {
def before = { println 'z' }
}
def x = new X()
def y = new Y()
def z = new Z()
assert x.respondsTo('before', null)
assert !y.respondsTo('before', null)
assert !z.respondsTo('before', null)
assert !x.hasProperty('before')
assert !y.hasProperty('before')
assert z.hasProperty('before')
x.before()
z.before()

Groovy DSL: setting properties in closure

I want to implement an 'active' flag for rules in my DSL. Here's how I wanted it to look like:
Shipping("Standard") {
active: true
description: "some text"
rules {
... define rules here
}
}
Here's how I got everything running following several tutorials:
Script dslScript = new GroovyShell().parse(new File("Standard"))
dslScript.metaClass.Shipping = { String name, Closure cl ->
ShippingDelegate delegate = new ShippingDelegate()
delegate.name = name
cl.delegate = delegate
cl.setResolveStrategy Closure.DELEGATE_FIRST
cl()
}
dslScript.run()
ShippingDelegate is simple:
class ShippingDelegate {
String name
void rules(Closure cl) {
... do stuff here
}
}
It all runs fine without complaints but how can I access 'active' or 'description'?
What does this syntax actually do anyway? It looks like a map assignment, but there is none. Or does the groovy compiler treat it as an incomplete ternary operator?
May I suggest a small change in your DSL so that your design can be simplified?
Edited, it is not clear in you example if you have more than one shipping instance. In my second try, I assume that the answer is yes
class ShippingRules {
boolean active
String description
String name
ShippingRules(String name) {
this.name=name
}
def rules(Closure c) {
c.delegate=this
c()
}
}
abstract class ShippingRulesScript extends Script {
def shipppingRules =[]
def shipping(String name, Closure c) {
def newRules=new ShippingRules(name)
shipppingRules << newRules
c.delegate=newRules
c()
}
}
def cfg= new CompilerConfiguration(
scriptBaseClass:ShippingRulesScript.name
)
Script dslScript = new GroovyShell(cfg).parse(new File("Standard"))
dslScript.run()
The DSL should be changed to this:
shipping("Standard") {
active= true
description= "some text"
rules {
... define rules here
}
}
shipping("International") {
active= true
description= "some text"
rules {
... define rules here
}
}
I.e. lose the capital to shipping, and use assignments instead of colons.
You would then later be able to retrieve the shipping rules from your dslScript shippingRules variable.
disclaimer: I cannot test my code right now, so there may be some typos in the code, but you get the general idea: use a base class where you provide your rules and properties to your script.
I asked a similar question on Google+, see here.
The summary is: you can use the map syntax only on constructors (ctors) and as function parameters.
Interesting is that it doesn't throw an exception.

how to detect caller instance in SoapUI groovy script?

A SoapUI project can run random script upon load.
Load Script is invoked with log and project variables.
In my shared lib I have method - addAsserts() that traverses the whole project and adds schema compliance assertions to SOAP test steps. In my Load Script I call shared method
addAsserts(this)
passing 'this' as a parameter and set closure.delegate to it inside addAsserts method to make 'project' variable accessible within the closure scope
addAsserts method is defined in sharedUtil.groovy:
static def addAsserts(that){
def closure={
project.testSuites.each { testSuiteName, testSuiteObject ->
testSuiteObject.testCases.each { testCaseName, testCaseObject ->
testCaseObject.testSteps.each { testStepName, testStepObject ->
if ("class com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep" == testStepObject.getClass().toString() ) {
log.info "adding 'Schema Compliance' assertion to ${testSuiteName}/${testCaseName}/${testStepName}"
testStepObject.addAssertion('Schema Compliance')
}
}
}
}
}//closure
closure.delegate=that // <--- i would like NOT to pass 'that' as parameter
// but rather detect in runtime with some kind of
// getCallerInstance() method
return closure.call()
}
QUESTION:
Is it possible to detect caller instance in runtime with some kind of getCallerInstance() method ?
No, I don't believe this is possible. Wasn't in Java either (you can find out the name/method of the calling class using some horrible stacktrace hacking, but not the instance of the class itself)
Edit...
It might be possible with a Category (but I am not experienced with SoapUI, so I don't know if this technique would fit)
Say we have a class Example defined like so:
class Example {
String name
}
We can then write a class very similar to your example code, which in this case will set the delegate of the closure, and the closure will print out the name property of the delegate (as we have set the resolve strategy to DELEGATE_ONLY)
class AssetAddingCategory {
static def addAsserts( that ) {
def closure = {
"Name of object: $name"
}
closure.delegate = that
closure.resolveStrategy = Closure.DELEGATE_ONLY
closure.call()
}
}
Later on in our code, it is then possible to do:
def tim = new Example( name:'tim' )
use( AssetAddingCategory ) {
println tim.addAsserts()
}
And this will print out
Name of object: tim

Resources