I am puzzling over Groovy delegation strategy in nested closures. Here is a simplified example:
class Clazz {
String name
String whoAmI() {
return name
}
void doit(Clazz clazz) {
def cl = {
println "Outer closure: resolveStrategy=${resolveStrategy}, " +
"implicit=${whoAmI()}, delegated=${delegate.whoAmI()}"
{->
println "Inner closure: resolveStrategy=${resolveStrategy}, " +
"implicit=${whoAmI()}, delegated=${delegate.whoAmI()}"
}.call()
}
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl.delegate = clazz
cl()
}
}
def a = new Clazz(name: 'A')
def b = new Clazz(name: 'B')
a.doit(b)
Output:
Outer closure: resolveStrategy=1, implicit=B, delegated=B
Inner closure: resolveStrategy=0, implicit=A, delegated=B
Why doesn't resolveStrategy propagate to the inner closure? The point of setting resolution strategy is to change the way implicit this is resolved. But if it doesn't propagate inside closures, then the mechanism seems to be as good as useless. Closures are so ubiquitous in Groovy that you can hardly write a couple of lines without them.
Found a similar question from almost five years ago: Nested closure resolution different between methods and properties? Apparently it's a bug, and it has been open without any movement since then: https://issues.apache.org/jira/browse/GROOVY-7232
The linked post notes something I also noticed: properties are correctly resolved to the delegate, but not method calls. Here is a reworked example to demonstrate this:
class Clazz {
String name
void doit(Clazz clazz) {
def cl = {
println "Outer closure: property=${name}, method=${getName()}"
{->
println "Inner closure: property=${name}, method=${getName()}"
}.call()
}
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl.delegate = clazz
cl()
}
}
def a = new Clazz(name: 'A')
def b = new Clazz(name: 'B')
a.doit(b)
Result:
Outer closure: property=B, method=B
Inner closure: property=B, method=A
Someone in the linked post posted a workaround using rehydrate. In my case though it works (sort of) only if all references are replaced, including this!
class Clazz {
String name
void doit(Clazz clazz) {
def cl = {
println "Outer closure: property=${name}, method=${getName()}"
{->
println "Inner closure: property=${name}, method=${getName()}"
}.call()
}
cl.rehydrate(clazz, clazz, clazz)()
}
}
def a = new Clazz(name: 'A')
def b = new Clazz(name: 'B')
a.doit(b)
Result:
Outer closure: property=B, method=B
Inner closure: property=B, method=B
Related
I've been experimenting with closures and the delegate scope. I've been sucessful in changing the delegate for "regular closures" but not when converting a method to a closure via the .& operator:
class Mother {
def method2() { nonexisting }
Closure method3 = {-> nonexisting}
}
Mother julia = new Mother()
// works for regular closures
def c3 = julia.method3
c3.delegate = [nonexisting: 'nonexisting']
assert 'nonexisting' == c3.call() // 'nonexisting' comes from the delegate Map
// fails for method reference closure
def c2 = julia.&method2
c2.delegate == [nonexisting: 'nonexisting']
println "this = ${c2.thisObject}"
println "owner = ${c2.owner}"
println "delegate = ${c2.delegate}"
assert 'nonexisting' == c2.call() // raises MissingPropertyException
Am I doing it wrong or it's just not possible to make the method reference closure to lookup free variables in my an object of my choice?
This is my closure method and i am looking for different ways to invoke this groovy method
myMethod(Closure c, def val) {
if(c)
c.call()
println val
}
Things i tried:
myMethod({/*code*/},"print something")
Is there a way i can skip braces or a better way to do the same?
Put the Closure last in the definition:
def myMethod(val, Closure c) {
if(c) c.call()
println val
}
Then you can do:
myMethod("print something") { -> println "closure!" }
Edit
Think you're going to need 2 methods:
def myMethod(Closure c) {
myMethod('default', c)
}
def myMethod(val, Closure c) {
if(c) c.call()
println val
}
Then you can do:
myMethod('tim') { println 'woo' }
or
myMethod { println 'woo' }
I'm implementing Groovy step definitions for Cucumber-JVM and I want a step to be able store itself so that the next step can repeat it n times.
Given(~'the (\\S+) is in the blender') { String thing ->
// do stuff...
context.repeatable = self.curry(thing)
}
What should "self" be in the above code?
I can't use "this" as that refers to the enclosing object (whatever that is in this case, maybe the script).
Since curry is a method of the Closure class, directly invoking curry applies to the closure, both if it is named:
def context
test = { String thing ->
context = curry(thing)
thing.toUpperCase()
}
test('hello')
println context.call()
test('world')
println context.call()
=>
HELLO
WORLD
or anonymous:
def context
['test'].each { String thing ->
context = curry(thing)
thing.toUpperCase()
}
println context.call()
=>
TEST
You can try using unreferenced curry method passing the received parameters:
clos = { String text ->
if (text) {
println "text=$text"
a = curry null
a()
} else {
println "done"
}
}
clos "closure text"
Will print:
text=closure text
done
Update
You can also use clone():
closure = {
def clone = clone()
}
I don't understand why xml."con:cred"."ser:user" = "modified_username" doesn't change the text. Can someone explain this?
input = """
<kuk:acc xmlns:kuk="kuk">
<con:cred xmlns:con="http://www.bea.com/wli/sb/resources/config">
<ser:user xmlns:ser="http://www.bea.com/wli/sb/services">username</ser:user>
</con:cred>
</kuk:acc>
"""
def xml = new XmlSlurper(keepWhitespace:true).parseText(input).declareNamespace(
ser:"http://www.bea.com/wli/sb/services",
con:"http://www.bea.com/wli/sb/resources/config")
println xml."con:cred"."ser:user"
xml."con:cred"."ser:user" = "modified_username" // That doesn't work
println xml."con:cred"."ser:user"
xml.cred.user = "modified_username" // That works
println xml."con:cred"."ser:user"
/*
def outputBuilder = new StreamingMarkupBuilder()
String result = outputBuilder.bind{ mkp.yield xml }
println result
*/
I've been digging in this problem some time and was about to ask just the same thing. Given that the method invoked when using the overloaded '=' operator is putAt(int, Object), a closer look into GPathResult code:
public void putAt(final int index, final Object newValue) {
final GPathResult result = (GPathResult)getAt(index);
if (newValue instanceof Closure) {
result.replaceNode((Closure)newValue);
} else {
result.replaceBody(newValue);
}
}
shows that replaceBody should be invoked. As *tim_yates* points out, replaceBody works well, so it seems that replaceNode is invoked instead (I cannot see why). Digging in NodeChildren's replaceNode, we can see that
protected void replaceNode(final Closure newValue) {
final Iterator iter = iterator();
while (iter.hasNext()) {
final NodeChild result = (NodeChild) iter.next();
result.replaceNode(newValue);
}
}
the closure never gets called, so nothing is done when replaceNode is invoked. So I think that there's a bug in replaceNode (it does nothing), and when doing xml."con:cred"."ser:user" = "modified_username" the right part of the expression is evaluated as a Closure (I need help in this point to understand why :-).
From Snipplr
Ok here is the script code, in the comments is the question and the exception thrown
class Class1 {
def closure = {
println this.class.name
println delegate.class.name
def nestedClos = {
println owner.class.name
}
nestedClos()
}
}
def clos = new Class1().closure
clos.delegate = this
clos()
//Now I want to add a new closure to Class1
def newClosure = {
println "new Closure"
println this.class.name
println delegate.class.name
def nestedClos = {
println owner.class.name
}
nestedClos()
}
//getAbc to create a property, not a method
Class1.metaClass.getAbc = newClosure
//What happens here is that the property abc is not used as a closure per se, it's used
//as a property and when I execute it just run the closure and when I want to change
//the delegate, a null pointer is thrown
clos = new Class1().abc //abc executed instead of passing the reference closure
clos.delegate = this //Exception!!!!
clos()
Ok, It's done it's not a fancy way, but I have the solution....yeah!
Create the property as object and later assign the closure
class Class1 {
def closure = {
println this.class.name
println delegate.class.name
def nestedClos = {
println owner.class.name
}
nestedClos()
}
}
def clos = new Class1().closure
clos.delegate = this
clos()
//Now I want to add a new closure to Class1
def newClosure = {
println "new Closure"
println this.class.name
println delegate.class.name
def nestedClos = {
println owner.class.name
}
nestedClos()
}
//before edit
//Class1.metaClass.abc = new Object()
Class1.metaClass.abc = newClosure
def cl = new Class1()
//Before edit
//For the sake of simplicity we are going to use & for the method
//clos = cl.abc
closs = cl.&abc
clos.delegate = this
clos()