Chapter 11
[ 279 ]
We can implement this structure by adding two closures to the binding called block
and nestedBlock. The block and nestedBlock closures accept a closure as their
only parameter. We saw in Chapter 5, Groovy Closures, how this is exactly the same
mechanism that is used to implement builders. The block and nestedBlock closures
need to manage their delegates in order to ensure that the expected binding scopes
are preserved.
binding.block = { closure ->
def cloned = closure.clone()
cloned.delegate = delegate
this.enclosing = "block"
println "block encountered"
cloned()
}
binding.nestedBlock = { closure ->
assert closure.delegate.enclosing == "block"
def cloned = closure.clone()
cloned.delegate = delegate
this.enclosing = "nestedBlock"
println "nested block encountered"
cloned()
}
block {
nestedBlock {
}
}
block {
nestedBlock {
}
}
In these examples, we cloned the passed-in closure before changing the
delegate. This is considered the best practice with a DSL, in case the
original closure is also used externally. This is also advisable if we make
any changes to the closure resolve strategy.