GROOVY-7579: Improve docs for invokeMethod (closes #528)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/8a17a1e2 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/8a17a1e2 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/8a17a1e2 Branch: refs/heads/parrot Commit: 8a17a1e27e0b4106d65ca0c5f0d511c51d0a58f7 Parents: aff9fff Author: John Wagenleitner <[email protected]> Authored: Sun Apr 23 16:06:13 2017 -0700 Committer: paulk <[email protected]> Committed: Thu May 11 08:03:50 2017 +1000 ---------------------------------------------------------------------- src/spec/doc/core-metaprogramming.adoc | 43 +++++++++++++++++------------ 1 file changed, 25 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/8a17a1e2/src/spec/doc/core-metaprogramming.adoc ---------------------------------------------------------------------- diff --git a/src/spec/doc/core-metaprogramming.adoc b/src/spec/doc/core-metaprogramming.adoc index 52344fd..4609d7f 100644 --- a/src/spec/doc/core-metaprogramming.adoc +++ b/src/spec/doc/core-metaprogramming.adoc @@ -21,19 +21,19 @@ = Metaprogramming -The Groovy language supports two flavors of metaprogramming: runtime metaprogramming and compile-time metaprogramming. -The first one allows altering the class model and the behavior of a program at runtime, while the second only occurs -at compile-time. Both have pros and cons, that we will detail in this section. +The Groovy language supports two flavors of metaprogramming: runtime and compile-time. +The first allows altering the class model and the behavior of a program at runtime while the second only occurs +at compile-time. Both have pros and cons that we will detail in this section. == Runtime metaprogramming -With runtime metaprogramming we can postpone to runtime the decision to intercept, inject and even synthesize methods of classes and interfaces. For a deep understanding of Groovy MOP we need to understand Groovy objects and Groovy's method handling. -In Groovy we work with three kinds of objects: POJO, POGO and Groovy Interceptors. Groovy allows metaprogramming for all types of objects but in different manner. +With runtime metaprogramming we can postpone to runtime the decision to intercept, inject and even synthesize methods of classes and interfaces. For a deep understanding of Groovy's metaobject protocol (MOP) we need to understand Groovy objects and Groovy's method handling. +In Groovy we work with three kinds of objects: POJO, POGO and Groovy Interceptors. Groovy allows metaprogramming for all types of objects but in a different manner. -- POJO - A regular Java object, whose class can be written in Java or any other language for the JVM. -- POGO - A Groovy object, whose class is written in Groovy. It extends `java.lang.Object` and implements the gapi:groovy.lang.GroovyObject[] interface by default. -- Groovy Interceptor - A Groovy object that implements the gapi:groovy.lang.GroovyInterceptable[] interface and has method-interception capability, which we'll discuss in the <<core-metaprogramming.adoc#_groovyinterceptable,GroovyInterceptable>> section. +- POJO - A regular Java object whose class can be written in Java or any other language for the JVM. +- POGO - A Groovy object whose class is written in Groovy. It extends `java.lang.Object` and implements the gapi:groovy.lang.GroovyObject[] interface by default. +- Groovy Interceptor - A Groovy object that implements the gapi:groovy.lang.GroovyInterceptable[] interface and has method-interception capability which is discussed in the <<core-metaprogramming.adoc#_groovyinterceptable,GroovyInterceptable>> section. -For every method call Groovy checks whether the object is a POJO or a POGO. For POJOs, Groovy fetches it's `MetaClass` from the gapi:groovy.lang.MetaClassRegistry[] and delegates method invocation to it. For POGOs, Groovy takes more steps, as illustrated in the following figure: +For every method call Groovy checks whether the object is a POJO or a POGO. For POJOs, Groovy fetches its `MetaClass` from the gapi:groovy.lang.MetaClassRegistry[] and delegates method invocation to it. For POGOs, Groovy takes more steps, as illustrated in the following figure: .Groovy interception mechanism image::assets/img/GroovyInterceptions.png[align="center"] @@ -62,14 +62,21 @@ public interface GroovyObject { ==== invokeMethod -According to the schema in <<core-metaprogramming.adoc#_runtime_metaprogramming,Runtime Metaprogramming>> this method is called when the method you called is not present on a Groovy object. -Here is a simple example using a overridden `invokeMethod()` method: +This method is primarily intended to be used in conjunction with the <<core-metaprogramming.adoc#_groovyinterceptable,GroovyInterceptable>> +interface or an object's `MetaClass` where it will intercept all method calls. + +It is also invoked when the method called is not present on a Groovy object. Here is a simple example using an +overridden `invokeMethod()` method: [source,groovy] ---- include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags=groovy_invoke_method,indent=0] ---- +However, the use of `invokeMethod` to intercept missing methods is discouraged. In cases where the intent is to only +intercept method calls in the case of a failed method dispatch use <<core-metaprogramming.adoc#_methodmissing,methodMissing>> +instead. + ==== get/setProperty Every read access to a property can be intercepted by overriding the `getProperty()` method of the current object. @@ -90,7 +97,7 @@ include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags ==== get/setMetaClass -You can a access an object's `metaClass` or set your own `MetaClass` implementation for changing the default interception mechanism. For example you can write your own implementation of the `MetaClass` interface and assign to it to objects and accordingly change the interception mechanism: +You can a access an object's `metaClass` or set your own `MetaClass` implementation for changing the default interception mechanism. For example, you can write your own implementation of the `MetaClass` interface and assign it to objects in order to change the interception mechanism: [source,groovy] ---- @@ -106,7 +113,7 @@ You can find an additional example in the <<core-metaprogramming.adoc#_groovyint === get/setAttribute -This functionality is related to the `MetaClass` implementation. In the default implementation you can access fields without invoking their getters and setters. The examples below demonstrate this approach: +This functionality is related to the `MetaClass` implementation. In the default implementation you can access fields without invoking their getters and setters. The examples below demonstrates this approach: [source, groovy] ---- @@ -121,7 +128,7 @@ include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags === methodMissing Groovy supports the concept of `methodMissing`. This method differs from `invokeMethod` in that it -is only invoked in case of a failed method dispatch, when no method can be found for the given name and/or the +is only invoked in the case of a failed method dispatch when no method can be found for the given name and/or the given arguments: [source,groovy] @@ -131,7 +138,7 @@ include::{projectdir}/src/spec/test/metaprogramming/MethodPropertyMissingTest.gr Typically when using `methodMissing` it is possible to cache the result for the next time the same method is called. -For example consider dynamic finders in GORM. These are implemented in terms of `methodMissing`. The code resembles +For example, consider dynamic finders in GORM. These are implemented in terms of `methodMissing`. The code resembles something like this: [source,groovy] @@ -194,7 +201,7 @@ package groovy.lang; public interface GroovyInterceptable extends GroovyObject { } ---- -When a Groovy object implements the `GroovyInterceptable` interface, it's `invokeMethod()` is called for any method calls. Below you can see a simple example of a object of this type: +When a Groovy object implements the `GroovyInterceptable` interface, its `invokeMethod()` is called for any method calls. Below you can see a simple example of a object of this type: [source,groovy] ---- @@ -209,9 +216,9 @@ include::{projectdir}/src/spec/test/metaprogramming/InterceptableTest.groovy[tag ---- [NOTE] -We cannot use default groovy methods like `println` because these methods are injected into all groovy objects so they will be intercepted too. +We cannot use default groovy methods like `println` because these methods are injected into all Groovy objects so they will be intercepted too. -If we want to intercept all methods call but do not want to implement the `GroovyInterceptable` interface we can implement `invokeMethod()` on an object's `MetaClass`. +If we want to intercept all method calls but do not want to implement the `GroovyInterceptable` interface we can implement `invokeMethod()` on an object's `MetaClass`. This approach works for both POGOs and POJOs, as shown by this example: [source,groovy]
