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