Skip to content
Permalink
Browse files
additional documentation clarifying closure delegation strategy in th…
…e presence of metaprogramming
  • Loading branch information
paulk-asert committed Mar 28, 2022
1 parent 2c0d757 commit fd1b6323c0a400643bbd072c8d53fe04fc5a5bc1
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 5 deletions.
@@ -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:
@@ -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`)
@@ -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

0 comments on commit fd1b632

Please sign in to comment.