You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2022/03/28 12:33:07 UTC

[groovy] branch GROOVY_4_0_X updated: additional documentation clarifying closure delegation strategy in the presence of metaprogramming

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new da927ee  additional documentation clarifying closure delegation strategy in the presence of metaprogramming
da927ee is described below

commit da927ee0478e70ada6e37895e80b8eb5b5059fd7
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Mar 28 20:19:18 2022 +1000

    additional documentation clarifying closure delegation strategy in the presence of metaprogramming
---
 src/spec/doc/core-closures.adoc           | 25 +++++++++++++++++++++++
 src/spec/doc/core-object-orientation.adoc |  3 ++-
 src/spec/test/ClosuresSpecTest.groovy     | 34 +++++++++++++++++++++++++++----
 3 files changed, 57 insertions(+), 5 deletions(-)

diff --git a/src/spec/doc/core-closures.adoc b/src/spec/doc/core-closures.adoc
index 5a94df5..62d56f9 100644
--- a/src/spec/doc/core-closures.adoc
+++ b/src/spec/doc/core-closures.adoc
@@ -365,6 +365,9 @@ of the delegate (resp. owner) does *not* have such a method or property:
 ----
 include::../test/ClosuresSpecTest.groovy[tags=delegate_only,indent=0]
 ----
+<1> for "owner first" it doesn't matter what the delegate is
+<2> for "delegate only" having `p` as the delegate succeeds
+<3> for "delegate only" having `t` as the delegate fails
 
 In this example, we define two classes which both have a `name` property but only the `Person` class declares an `age`.
 The `Person` class also declares a closure which references `age`. We can change the default resolution strategy from
@@ -376,6 +379,28 @@ the `Person` class, the owner is not used.
 NOTE: A comprehensive explanation about how to use this feature to develop DSLs can be found in a
 link:core-domain-specific-languages.html[dedicated section of the manual].
 
+==== Delegation strategy in the presence of metaprogramming
+
+When describing the "owner first" delegation strategy we spoke
+about using a property/method from the owner if it "_existed_" otherwise
+using the respective property/method from the delegate. And a similar
+story for "delegate first" but in reverse. Instead of using the word
+"_existed_", it would have been more accurate to use the wording "_handled_".
+That means that for "owner first", if the property/method exists in
+the owner, or it has a propertyMissing/methodMissing hook, then the owner
+will handle the member access.
+
+We can see this in action with a slightly altered version of our previous example:
+
+[source,groovy]
+----
+include::../test/ClosuresSpecTest.groovy[tags=delegate_only_prop_missing,indent=0]
+----
+
+In this example, even though our instance of the `Thing` class (our delegate for the last use of `cl`) has no `age` property,
+the fact that it handles the missing property via its `propertyMissing` hook,
+means that `age` will be `-1`.
+
 == Closures in GStrings
 
 Take the following code:
diff --git a/src/spec/doc/core-object-orientation.adoc b/src/spec/doc/core-object-orientation.adoc
index f936abe..37743b4 100644
--- a/src/spec/doc/core-object-orientation.adoc
+++ b/src/spec/doc/core-object-orientation.adoc
@@ -669,7 +669,8 @@ include::../test/objectorientation/MethodsTest.groovy[tags=checked_method_declar
 [[fields]]
 ==== Fields
 
-A field is a member of a class or a trait which has:
+A field is a member of a class, interface or trait which stores data.
+A field defined in a Groovy source file has:
 
 * a mandatory _access modifier_ (`public`, `protected`, or `private`)
 * one or more optional _modifiers_ (`static`, `final`, `synchronized`)
diff --git a/src/spec/test/ClosuresSpecTest.groovy b/src/spec/test/ClosuresSpecTest.groovy
index 05fc9bf..77307b3 100644
--- a/src/spec/test/ClosuresSpecTest.groovy
+++ b/src/spec/test/ClosuresSpecTest.groovy
@@ -365,15 +365,16 @@ class ClosuresSpecTest extends GroovyTestCase {
             def t = new Thing(name:'Printer')
             def cl = p.fetchAge
             cl.delegate = p
-            assert cl() == 42
+            assert cl() == 42          // <1>
             cl.delegate = t
-            assert cl() == 42
+            assert cl() == 42          // <1>
+
             cl.resolveStrategy = Closure.DELEGATE_ONLY
             cl.delegate = p
-            assert cl() == 42
+            assert cl() == 42          // <2>
             cl.delegate = t
             try {
-                cl()
+                cl()                   // <3>
                 assert false
             } catch (MissingPropertyException ex) {
                 // "age" is not defined on the delegate
@@ -382,6 +383,31 @@ class ClosuresSpecTest extends GroovyTestCase {
         '''
     }
 
+    void testDelegateOnlyPropertyMissing() {
+        assertScript '''
+            // tag::delegate_only_prop_missing[]
+            class Person {
+                String name
+                int age
+                def fetchAge = { age }
+            }
+            class Thing {
+                String name
+                def propertyMissing(String name) { -1 }
+            }
+
+            def p = new Person(name:'Jessica', age:42)
+            def t = new Thing(name:'Printer')
+            def cl = p.fetchAge
+            cl.resolveStrategy = Closure.DELEGATE_FIRST
+            cl.delegate = p
+            assert cl() == 42
+            cl.delegate = t
+            assert cl() == -1
+            // end::delegate_only_prop_missing[]
+        '''
+    }
+
     void testGStringEager() {
         // tag::gstring_eager_intro[]
         def x = 1