Groovy for Domain-specific Languages - Second Edition

(nextflipdebug2) #1
Chapter 7

[ 177 ]

No matter how far you look in the documentation for MarkupBuilder, you won't
find anything about it having a customers method. So what's happening when
we write:


def customers = builder.customers {
...?

The answer is that MarkupBuilder is pretending to have a method with the
signature: MarkupBuilder.customers(Closure c). In the next line of code,
things get a little more interesting. This line of code is defined within the body
of the closure itself:


customer(id:1001) {
...

To explain this, we need to understand how closures handle method calls. When a
closure encounters a method call that it cannot handle itself, it automatically relays
the invocation to its owner object. If this fails, it relays the invocation to its delegate.
Normally, the delegate would be the enclosing script or class, but MarkupBuilder
sets the delegate object to itself. The closure relays the customer method invocation
to MarkupBuilder, which has an invokeMethod() implementation that pretends to
have a method MarkupBuilder.customer(Map m, Closure c).


Method invocation and property lookup are governed by the resolve strategy of
the Closure. The resolve strategy tells Closure what objects it should look at when
attempting to resolve a method or property reference. By default, the resolve strategy
is set to OWNER_FIRST, which means that the first place we look is in owner. If this
lookup fails, then the search continues to the delegate object.


MarkupBuilder relies on the default resolve strategy, but we can change the resolve
strategy as the need arises. The full list of resolve strategies is as follows:



  • OWNER_FIRST (the default): This resolves methods and properties in owner
    first followed by delegate if not found.

  • DELEGATE_FIRST: This resolves in delegate first and then searches owner if
    not found.

  • OWNER_ONLY: This resolves in owner only and doesn't search delegate.

  • DELEGATE_ONLY: This resolves in delegate only with no search of owner.

  • TO_SELF: This is a special case to allow getProperty of Closure itself to be
    overridden. With this resolve strategy, the closure calls getProperty on itself
    first before continuing the lookup through the normal lookup process.

Free download pdf