Repository: incubator-groovy Updated Branches: refs/heads/GROOVY_2_4_X 509d22c1d -> f6d6c2c1c
Add Runtime Metaprogramming and GroovyObject section Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/fb4b17bd Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/fb4b17bd Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/fb4b17bd Branch: refs/heads/GROOVY_2_4_X Commit: fb4b17bda6f8d92c82a6ca980c6917f260de5dfc Parents: 509d22c Author: Maksym Stavytskyi <stavyts...@gmail.com> Authored: Mon Mar 30 01:51:50 2015 +0300 Committer: pascalschumacher <pascalschumac...@gmx.net> Committed: Sun May 3 18:36:23 2015 +0200 ---------------------------------------------------------------------- src/spec/assets/img/GroovyInterceptions.png | Bin 0 -> 35387 bytes src/spec/doc/core-metaprogramming.adoc | 72 ++++++++++++++++--- .../metaprogramming/GroovyObjectTest.groovy | 70 ++++++++++++++++++ 3 files changed, 133 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/fb4b17bd/src/spec/assets/img/GroovyInterceptions.png ---------------------------------------------------------------------- diff --git a/src/spec/assets/img/GroovyInterceptions.png b/src/spec/assets/img/GroovyInterceptions.png new file mode 100644 index 0000000..b56cbc3 Binary files /dev/null and b/src/spec/assets/img/GroovyInterceptions.png differ http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/fb4b17bd/src/spec/doc/core-metaprogramming.adoc ---------------------------------------------------------------------- diff --git a/src/spec/doc/core-metaprogramming.adoc b/src/spec/doc/core-metaprogramming.adoc index adf8a7b..b1e48b1 100644 --- a/src/spec/doc/core-metaprogramming.adoc +++ b/src/spec/doc/core-metaprogramming.adoc @@ -5,28 +5,85 @@ The first one allows altering the class model and the behavior of a program at r 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 deep understanding of Groovy MOP we need to understand Groovy objects and Groovy's methods handling. +In groovy we work with three kinds of objects: POJO(any java objects), POGO and Groovy interceptors. Groovy allows metaprogramming for all types of objects but in different manner. -(TBD) +- POJO - is regular java object, which class can be created by Java or other language for the JVM. +- POGO - is object, which class is written on Groovy, it extends `java.lang.Object` and implements `groovy.lang.GroovyObject` interface. +- Groovy interceptor - is Groovy object that implement `GroovyInterceptable` interface and have method-interception capability, which we'll discuss in the <<core-metaprogramming.adoc#_groovyinterceptable,GroovyInterceptable>> topic. + +For every method's call Groovy checks whether the objects is POJO or POGO. For POJO, Groovy fetches its `MetaClass` from `MetaClassRegistry` and delegates method invocation to it. For POGO, Groovy makes more steps, as illustrated in the following figure. +.Groovy interception mechanism +image::assets/img/GroovyInterceptions.png[align="center"] === GroovyObject interface (MaksymStavytskyi) + +The `GroovyObject` is main interface in Groovy as `Object` class in java. `GroovyObject` has default implementation `GroovyObjectSupport` class and it is responsible to transfer invocation to `MetaClass` object. `GroovyObject` source looks like: + +[source, groovy] +---- +package groovy.lang; + +public interface GroovyObject { + + Object invokeMethod(String name, Object args); + + Object getProperty(String propertyName); + + void setProperty(String propertyName, Object newValue); + + MetaClass getMetaClass(); + + void setMetaClass(MetaClass metaClass); +} +---- + ==== invokeMethod -(TBD) +Regarding to schema in the <<core-metaprogramming.adoc#_runtime_metaprogramming,Runtime Metaprogramming>> this method is called when the method which you called isn't presented on Groovy object. + +[source,groovy] +---- +include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags=groovy_invoke_method,indent=0] +---- ==== get/setProperty -(TBD) +All methods will be intercepted by property interception mechanism. So all requests to field values will be intercepted by `getProperty()` method of current object. +//TODO Prepare property interception mechanism schema. +[source, groovy] +---- +include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags=groovy_get_property,indent=0] +---- +<1> This string is responsible to run right getter to another fields except `field3`. + +In the same way you can intercept setting fields. ==== get/setMetaClass -(TBD) +You can get access to object's `metaClass` or set own `MetaClass` implementation for changing default interception mechanism. As example you can write own implementation of `MetaClass` interface and assign to own objects and accordingly change groovy interception mechanism. + +[source,groovy] +---- +// getMetaclass +someObject.metaClass +// setMetaClass +someObject.metaClass = new OwnMetaClassImplementation() +---- + +[NOTE] +You can find additional exemple in the <<core-metaprogramming.adoc#_groovyinterceptable,GroovyInterceptable>> topic. === get/setAttribute -(TBD) +This functionality related to `MetaClass` implementation. In default implementation you can receive field's value without invocation its getter and setters. Example below shows this approach. +[source, groovy] +---- +include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags=groovy_get_attribute,indent=0] +---- === methodMissing @@ -93,10 +150,7 @@ performance. `methodMissing` and `propertyMissing` that deal with static methods and properties can be added via the <<core-metaprogramming.adoc#metaprogramming_emc,ExpandoMetaClass>>. -=== GroovyInterceptable - -(TBD) - +=== GroovyInterceptable (TBD) [[categories]] === Categories http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/fb4b17bd/src/spec/test/metaprogramming/GroovyObjectTest.groovy ---------------------------------------------------------------------- diff --git a/src/spec/test/metaprogramming/GroovyObjectTest.groovy b/src/spec/test/metaprogramming/GroovyObjectTest.groovy new file mode 100644 index 0000000..39f77ab --- /dev/null +++ b/src/spec/test/metaprogramming/GroovyObjectTest.groovy @@ -0,0 +1,70 @@ +package metaprogramming + +class GroovyObjectTest extends GroovyTestCase{ + void testInvokeMethod() { + assertScript ''' +// tag::groovy_invoke_method[] +class SomeGroovyClass { + def invokeMethod(String name, Object args){ + return "called invokeMethod $name $args" + } + + def test(){ + return 'exist method' + } +} + +def someGroovyClass = new SomeGroovyClass() +assert someGroovyClass.test() == 'exist method' +assert someGroovyClass.someMethod() == 'called invokeMethod someMethod []' +// end::groovy_invoke_method[] +''' + } + + void testGetProperty (){ + assertScript ''' +// tag::groovy_get_property[] +class SomeGroovyClass { + def field1 = 'ha' + def field2 = 'ho' + def field4 = 'hu' + public def getField1(){ + return 'getHa' + } + + @Override + def getProperty(String name) { + if (name != 'field3') + return metaClass.getProperty(this, name) // <1> + else + return 'field3' + } +} + +def someGroovyClass = new SomeGroovyClass() +assert someGroovyClass.'field1' == 'getHa' +assert someGroovyClass.'field2' == 'ho' +assert someGroovyClass.'field3' == 'field3' +assert someGroovyClass.field4 == 'hu' +// end::groovy_get_property[] +''' + } + + void testGetAttribute (){ + assertScript ''' +// tag::groovy_get_attribute[] +class SomeGroovyClass { + def field1 = 'ha' + def field2 = 'ho' + public def getField1(){ + return 'getHa' + } +} + +def someGroovyClass = new SomeGroovyClass() +assert someGroovyClass.metaClass.getAttribute(someGroovyClass, 'field1') == 'ha' +assert someGroovyClass.metaClass.getAttribute(someGroovyClass, 'field2') == 'ho' +// end::groovy_get_attribute[] +''' + } +} \ No newline at end of file