Documentation: some additions/improvements to the "runtime metaprogramming" 
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/e01517d7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/e01517d7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/e01517d7

Branch: refs/heads/master
Commit: e01517d7658de8e31dc5aee3ba9a0ec0c2faadb0
Parents: 2d42a54
Author: pascalschumacher <pascalschumac...@gmx.net>
Authored: Sun May 3 18:28:37 2015 +0200
Committer: pascalschumacher <pascalschumac...@gmx.net>
Committed: Sun May 3 18:35:31 2015 +0200

----------------------------------------------------------------------
 src/spec/doc/core-metaprogramming.adoc          | 85 +++++++++++---------
 .../metaprogramming/GroovyObjectTest.groovy     | 78 ++++++++++++++----
 .../metaprogramming/InterceptableTest.groovy    | 22 ++---
 .../InterceptionThroughMetaClassTest.groovy     |  7 +-
 .../MethodPropertyMissingTest.groovy            |  1 +
 5 files changed, 131 insertions(+), 62 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/e01517d7/src/spec/doc/core-metaprogramming.adoc
----------------------------------------------------------------------
diff --git a/src/spec/doc/core-metaprogramming.adoc 
b/src/spec/doc/core-metaprogramming.adoc
index 81d1c04..86d8317 100644
--- a/src/spec/doc/core-metaprogramming.adoc
+++ b/src/spec/doc/core-metaprogramming.adoc
@@ -5,23 +5,23 @@ 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.
+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.
 
-- 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.
+- 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.
 
-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.
+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:
 
 .Groovy interception mechanism
 image::assets/img/GroovyInterceptions.png[align="center"]
 
-=== GroovyObject interface (MaksymStavytskyi)
+=== GroovyObject interface
 
-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:
+gapi:groovy.lang.GroovyObject[] is the main interface in Groovy as the 
`Object` class is in Java. `GroovyObject` has a default implementation in the 
gapi:groovy.lang.GroovyObjectSupport[] class and it is responsible to transfer 
invocation to the gapi:groovy.lang.MetaClass[] object. The `GroovyObject` 
source looks like this:
 
-[source, groovy]
+[source, java]
 ----
 package groovy.lang;
 
@@ -41,7 +41,8 @@ public interface GroovyObject {
 
 ==== invokeMethod
 
-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.
+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 overriden `invokeMethod()` method:
 
 [source,groovy]
 ----
@@ -50,19 +51,25 @@ 
include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags
 
 ==== get/setProperty
 
-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.
+Every read access to a property can be intercepted by overriding the 
`getProperty()` method of the current object.
+Here is a simple example:
+
 [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`.  
+<1> Forwards the request to the getter for all properties except `field3`.  
 
-In the same way you can intercept setting fields.
+You can intercept write access to properties by overriding the `setProperty()` 
method:
+
+[source, groovy]
+----
+include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags=groovy_gset_property,indent=0]
+----
 
 ==== get/setMetaClass
 
-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.
+You can a access a objects `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:
 
 [source,groovy]
 ----
@@ -74,30 +81,34 @@ someObject.metaClass = new OwnMetaClassImplementation()
 ----
 
 [NOTE]
-You can find additional exemple in the 
<<core-metaprogramming.adoc#_groovyinterceptable,GroovyInterceptable>> topic.
+You can find an additional example in the 
<<core-metaprogramming.adoc#_groovyinterceptable,GroovyInterceptable>> topic.
 
 === get/setAttribute
 
-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.
+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:
 
 [source, groovy]
 ----
 
include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags=groovy_get_attribute,indent=0]
 ----
 
+[source, groovy]
+----
+include::{projectdir}/src/spec/test/metaprogramming/GroovyObjectTest.groovy[tags=groovy_set_attribute,indent=0]
+----
+
 === methodMissing
 
 Groovy supports the concept of `methodMissing`. This method differs from 
`invokeMethod` in that it
-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.
+is only invoked in case of a failed method dispatch, when no method can be 
found for the given name and/or the
+given arguments:
 
 [source,groovy]
 ----
 
include::{projectdir}/src/spec/test/metaprogramming/MethodPropertyMissingTest.groovy[tags=method_missing_simple,indent=0]
 ----
 
-Typically when using `methodMissing` the code will react in some way that 
makes it possible for the next time the same
-method is called, that it goes through the regular Groovy method dispatch 
logic.
+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
 something like this:
@@ -105,7 +116,9 @@ something like this:
 [source,groovy]
 ----
 class GORM {
+
    def dynamicMethods = [...] // an array of dynamic methods that use regex
+   
    def methodMissing(String name, args) {
        def method = dynamicMethods.find { it.match(name) }
        if(method) {
@@ -119,14 +132,14 @@ class GORM {
 }
 ----
 
-Notice how, if we find a method to invoke then we dynamically register a new 
method on the fly using `ExpandoMetaClass`.
-This is so that the next time the same method is called it is more efficient. 
This way `methodMissing` doesn't have
-the overhead of `invokeMethod` _and_ is not expensive for the second call.
+Notice how, if we find a method to invoke, we then dynamically register a new 
method on the fly using 
<<core-metaprogramming.adoc#metaprogramming_emc,ExpandoMetaClass>>.
+This is so that the next time the same method is called it is more efficient. 
This way of using `methodMissing` does not have
+the overhead of `invokeMethod` _and_ is not expensive from the second call on.
 
 === propertyMissing
 
 Groovy supports the concept of `propertyMissing` for intercepting otherwise 
failing property resolution attempts. In the
-case of a getter method, `propertyMissing` takes a single String argument 
resembling the property name:
+case of a getter method, `propertyMissing` takes a single `String` argument 
containing the property name:
 
 [source,groovy]
 ----
@@ -136,7 +149,7 @@ 
include::{projectdir}/src/spec/test/metaprogramming/MethodPropertyMissingTest.gr
 The `propertyMissing(String)` method is only called when no getter method for 
the given property can be found by the Groovy
 runtime.
 
-For a setter methods a second `propertyMissing` definition can be added that 
takes an additional value argument:
+For setter methods a second `propertyMissing` definition can be added that 
takes an additional value argument:
 
 [source,groovy]
 ----
@@ -147,30 +160,27 @@ As with `methodMissing` it is best practice to 
dynamically register new properti
 performance.
 
 [NOTE]
-`methodMissing` and `propertyMissing` that deal with static methods and 
properties can be added via
+`methodMissing` and `propertyMissing` methods that deal with static methods 
and properties can be added via
 the <<core-metaprogramming.adoc#metaprogramming_emc,ExpandoMetaClass>>.
 
 === GroovyInterceptable
-`GroovyInterceptable` interface is marker interface that extends 
`GroovyObject` and is used to notify groovy runtime that all methods should be 
intercepted through the method dispatcher mechanism of groovy runtime.
+The gapi:groovy.lang.GroovyInterceptable[] interface is marker interface that 
extends `GroovyObject` and is used to notify the Groovy runtime that all 
methods should be intercepted through the method dispatcher mechanism of the 
Groovy runtime.
 
-[source, groovy]
+[source, java]
 ----
 package groovy.lang;
 
 public interface GroovyInterceptable extends GroovyObject {
-  
 }
 ----
-When groovy object implements the `GroovyInterceptable` interface, then its 
`invokeMethod()` is called for any method's calls. Below you can see a simple 
example of object of this type.
-
-//TODO Add information about println method. Default groovy object we cannot 
use with `GroovyInterceptable` interface becaues calling these methods will be 
intercepted too and we will have `StackOverflowError`.
+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:
 
 [source,groovy]
 ----
 
include::{projectdir}/src/spec/test/metaprogramming/InterceptableTest.groovy[tags=groovy_interceptable_object_example,indent=0]
 ----
 
-Next piece of code is test which shows that both calls of existed and 
nonexisted methods will return the same value.
+The next piece of code is a test which shows that both calls to existing and 
non-existing methods will return the same value.
 
 [source,groovy]
 ----
@@ -178,9 +188,10 @@ 
include::{projectdir}/src/spec/test/metaprogramming/InterceptableTest.groovy[tag
 ----
 
 [NOTE]
-We cannot use default groovy methods like as `println` because this methods 
are injected to 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 don't want to implement 
`GroovyInterceptable` interface we can implement `invokeMethod()` on an 
object's `MetaClass`. This approach works for both types POGO and POJO. 
+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`.
+This approach works for both POGOs and POJOs, as shown by this example:
 
 [source,groovy]
 ----
@@ -188,7 +199,7 @@ 
include::{projectdir}/src/spec/test/metaprogramming/InterceptionThroughMetaClass
 ----
 
 [NOTE]
-Additional information about `MetaClass` you can find in the 
<<core-metaprogramming.adoc#_metaclasses_tbd,MetaClasses>> topic.
+Additional information about `MetaClass` can be found in the 
<<core-metaprogramming.adoc#_metaclasses,MetaClasses>> section.
 
 [[categories]]
 === Categories

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/e01517d7/src/spec/test/metaprogramming/GroovyObjectTest.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/metaprogramming/GroovyObjectTest.groovy 
b/src/spec/test/metaprogramming/GroovyObjectTest.groovy
index 39f77ab..3131fa7 100644
--- a/src/spec/test/metaprogramming/GroovyObjectTest.groovy
+++ b/src/spec/test/metaprogramming/GroovyObjectTest.groovy
@@ -1,38 +1,41 @@
 package metaprogramming
 
-class GroovyObjectTest extends GroovyTestCase{
+class GroovyObjectTest extends GroovyTestCase {
     void testInvokeMethod() {
         assertScript '''
 // tag::groovy_invoke_method[]
 class SomeGroovyClass {
-    def invokeMethod(String name, Object args){
+
+    def invokeMethod(String name, Object args) {
         return "called invokeMethod $name $args"
     }
     
-    def test(){
-        return 'exist method'
+    def test() {
+        return 'method exists'
     }
 }
 
 def someGroovyClass = new SomeGroovyClass()
-assert someGroovyClass.test() == 'exist method'
+
+assert someGroovyClass.test() == 'method exists'
 assert someGroovyClass.someMethod() == 'called invokeMethod someMethod []'
 // end::groovy_invoke_method[]
 '''
     }
     
-    void testGetProperty (){
+    void testGetProperty () {
         assertScript '''
 // tag::groovy_get_property[]
 class SomeGroovyClass {
-    def field1 = 'ha'
+
+    def property1 = 'ha'
     def field2 = 'ho'
     def field4 = 'hu'
-    public def getField1(){
+    
+    def getField1() {
         return 'getHa'
     }
     
-    @Override
     def getProperty(String name) {
         if (name != 'field3')
             return metaClass.getProperty(this, name) // <1>
@@ -42,29 +45,76 @@ class SomeGroovyClass {
 }
 
 def someGroovyClass = new SomeGroovyClass()
-assert someGroovyClass.'field1' == 'getHa'
-assert someGroovyClass.'field2' == 'ho'
-assert someGroovyClass.'field3' == 'field3'
+
+assert someGroovyClass.field1 == 'getHa'
+assert someGroovyClass.field2 == 'ho'
+assert someGroovyClass.field3 == 'field3'
 assert someGroovyClass.field4 == 'hu'
 // end::groovy_get_property[]
 '''
     }
+    
+    void testSetProperty () {
+        assertScript '''
+// tag::groovy_set_property[]
+class POGO {
 
-    void testGetAttribute (){
+    String property 
+    
+    void setProperty(String name, Object value) {
+        this.@"$name" = 'overriden'
+    }
+}
+
+def pogo = new POGO()
+pogo.property = 'a'
+
+assert pogo.property == 'overriden'
+// end::groovy_set_property[]
+'''
+    }
+
+    void testGetAttribute () {
         assertScript '''
 // tag::groovy_get_attribute[]
 class SomeGroovyClass {
+
     def field1 = 'ha'
     def field2 = 'ho'
-    public def getField1(){
+    
+    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[]
 '''
     }
+    
+    void testSetAttribute () {
+        assertScript '''
+// tag::groovy_set_attribute[]
+class POGO {
+
+    private String field
+    String property1
+    
+    void setProperty1(String property1) {
+        this.property1 = "setProperty1"
+    }
+}
+
+def pogo = new POGO()
+pogo.metaClass.setAttribute(pogo, 'field', 'ha')
+pogo.metaClass.setAttribute(pogo, 'property1', 'ho')
+
+assert pogo.field == 'ha'
+assert pogo.property1 == 'ho'
+// end::groovy_set_attribute[]
+'''
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/e01517d7/src/spec/test/metaprogramming/InterceptableTest.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/metaprogramming/InterceptableTest.groovy 
b/src/spec/test/metaprogramming/InterceptableTest.groovy
index 3950e74..4e27ce8 100644
--- a/src/spec/test/metaprogramming/InterceptableTest.groovy
+++ b/src/spec/test/metaprogramming/InterceptableTest.groovy
@@ -2,19 +2,23 @@ package metaprogramming
 
 // tag::groovy_interceptable_test[]
 class InterceptableTest extends GroovyTestCase {
-       void testCheckInterception() {
-               def interception = new Interception()
-               assertEquals interception.definedMethod(), 
interception.someMethod()
-       }
+
+    void testCheckInterception() {
+        def interception = new Interception()
+
+        assert interception.definedMethod() == 'invokedMethod'
+        assert interception.someMethod() == 'invokedMethod'
+    }
 }
 // end::groovy_interceptable_test[]
 
 // tag::groovy_interceptable_object_example[]
 class Interception implements GroovyInterceptable {
-       def definedMethod() {}
-       @Override
-       def invokeMethod(String name, Object args) {
-               'invokedMethod'
-       }
+
+    def definedMethod() { }
+
+    def invokeMethod(String name, Object args) {
+        'invokedMethod'
+    }
 }
 // end::groovy_interceptable_object_example[]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/e01517d7/src/spec/test/metaprogramming/InterceptionThroughMetaClassTest.groovy
----------------------------------------------------------------------
diff --git 
a/src/spec/test/metaprogramming/InterceptionThroughMetaClassTest.groovy 
b/src/spec/test/metaprogramming/InterceptionThroughMetaClassTest.groovy
index 133754c..ace80a3 100644
--- a/src/spec/test/metaprogramming/InterceptionThroughMetaClassTest.groovy
+++ b/src/spec/test/metaprogramming/InterceptionThroughMetaClassTest.groovy
@@ -4,20 +4,23 @@ import groovy.xml.Entity
 
 // tag::meta_class_interception[]
 class InterceptionThroughMetaClassTest extends GroovyTestCase {
+
     void testPOJOMetaClassInterception() {
         String invoking = 'ha'
-        invoking.metaClass.invokeMethod = {String name, Object args ->
+        invoking.metaClass.invokeMethod = { String name, Object args ->
             'invoked'
         }
+        
         assert invoking.length() == 'invoked'
         assert invoking.someMethod() == 'invoked'
     }
 
     void testPOGOMetaClassInterception() {
         Entity entity = new Entity('Hello')
-        entity.metaClass.invokeMethod = {String name, Object args ->
+        entity.metaClass.invokeMethod = { String name, Object args ->
             'invoked'
         }
+        
         assert entity.build(new Object()) == 'invoked'
         assert entity.someMethod() == 'invoked'
     }

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/e01517d7/src/spec/test/metaprogramming/MethodPropertyMissingTest.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/metaprogramming/MethodPropertyMissingTest.groovy 
b/src/spec/test/metaprogramming/MethodPropertyMissingTest.groovy
index 7cf45bc..beac888 100644
--- a/src/spec/test/metaprogramming/MethodPropertyMissingTest.groovy
+++ b/src/spec/test/metaprogramming/MethodPropertyMissingTest.groovy
@@ -24,6 +24,7 @@ class MethodPropertyMissingTest extends GroovyTestCase {
         assertScript '''
             //tag::method_missing_simple[]
             class Foo {
+            
                def methodMissing(String name, def args) {
                     return "this is me"
                }

Reply via email to