Fiftoine
Fiftoine

Reputation: 291

Groovy MetaProgramming - intercept all method even missing ones

I'd like to intercept all method (instance and static) of a class even the missing ones.

Let say that :

class SomeClass {
    def methodMissing(String name, args) {
        if(name == "unknownMethod"){
            return "result from unknownMethod"
        }
        else throw new MissingMethodException(name, delegate, args)
    }
}

SomeClass.metaClass.static.invokeMethod = { methodName, args ->
    println "Before"
    def result = delegate.metaClass.invokeMethod(delegate, methodName, *args)
    println "After"
    return result
}

new SomeClass().with{ sc ->
    sc.unknownMethod()  //throw the MissingMethodExcept
}

This works well for method that are implemented by the class but when it's a method handled by methodMissing, I get a MissingMethodException...

How would you do that?

Thanks in advance

Upvotes: 2

Views: 2318

Answers (1)

tim_yates
tim_yates

Reputation: 171084

I think you need to catch the non static invokeMethod as well

Also, you need to go through getMetaMethod to call the original method or else you run the risk of stackoverflows.

Given the following:

class SomeClass {
  String name

  static String joinWithCommas( a, b, c ) {
    [ a, b, c ].join( ',' )
  }

  String joinAfterName( a, b, c ) {
    "$name : ${SomeClass.joinWithCommas( a, b, c )}"
  }

  def methodMissing(String name, args) {
    if(name == "unknownMethod"){
      return "result from unknownMethod"
    }
    else {
      throw new MissingMethodException( name, SomeClass, args )
    }
  }
}

// Return a closure for invoke handler for a class
// with a given title (for the logging)
def invokeHandler = { clazz, title ->
  { String methodName, args ->
    println "Before $methodName ($title)"
    def method = clazz.metaClass.getMetaMethod( methodName, args )
    def result = method == null ?
                   clazz.metaClass.invokeMissingMethod( delegate, methodName, args ) :
                   method.invoke( delegate, args )
    println "After $methodName result = $result"
    result 
  }
}

SomeClass.metaClass.invokeMethod = invokeHandler( SomeClass, 'instance' )
SomeClass.metaClass.static.invokeMethod = invokeHandler( SomeClass, 'static' )


new SomeClass( name:'tim' ).with { sc ->
  sc.joinAfterName( 'a', 'b', 'c' )
  sc.unknownMethod( 'woo', 'yay' )
  sc.cheese( 'balls' )
}

I get the output:

Before with (instance)
Before joinAfterName (instance)
Before joinWithCommas (static)
After joinWithCommas result = a,b,c
After joinAfterName result = tim : a,b,c
Before unknownMethod (instance)
After unknownMethod result = result from unknownMethod
Before cheese (instance)
Exception thrown

groovy.lang.MissingMethodException: No signature of method: SomeClass.cheese() is applicable for argument types: (java.lang.String) values: [balls]

Upvotes: 2

Related Questions