groovymetaprogrammingexpandometaclass

Passing delegate through nested closures in Groovy


I am creating a builder which accepts Groovy closures as markup. However I am having trouble catching method calls with nested closures.

Closure nested = {
   foo ()       //will throw missingMethod exception
}
Closure root = {
   foo ()       //prints 'missing foo []'
   inline_nested {
     foo ()     //prints 'missing foo []'
   }
   nested ()    
}
builder.execute (root)

// ...
class MyBuilder {
  void execute (Closure closure) {
    def clone = closure.clone()
    clone.delegate = this
    clone()
  }
  def missingMethod (String name, args) {
     println "missing ${name} ${args}"
  }
}

Is there any way I can set the delegate property for nested closures?


Solution

  • i'd go with no, you cant. and you probably don't need it.
    first. you are either the owner or the delegate of a closure. if you directly call a closure defined somewhere else, the call is resolved without your builder assistance.
    second. do you really need that nested()? i believe you could easily use execute nested instead

    here's an example of what i mean

    def nested2 = {
      someMethodAtNested2 '3'
      println "nested2! - $it"
    }
    def nested1 = {arg1,arg2->
      someMethodAtNested1 '2'
      println "nested1! - $arg1"
      include nested2, arg2
    }
    def root = {
      someMethodAtRoot '1'
      println "root!"
      include nested1, 'param1', 'param2'
    }
    new FooBuilder().build root
    
    class FooBuilder {
      void build(Closure closure) {
        include closure
      }
      def include(Closure closure, ...args) {
        def clone = closure.clone()
        clone.delegate = this
        clone.resolveStrategy = Closure.DELEGATE_FIRST
        clone(*args)
      }
      def methodMissing(String name, args) {
        println "missing ${name} ${args}"
      }
    }
    

    as a side note, i don't think builder support is the way to go. it might be useful for creating builders in java. but pure groovy is far easier. at least for small to medium complexity builders (never wrote a really large one).
    you do need some knowledge of groovy's method dispatch process though