groovyclosuresexpando

Inject a Groovy Closure into Expando that acts as Property


I have a class that extends Expando and gets injected with dynamic properties.

class Dynamo extends Expando {
}

Dynamo dynamic = [ firstName: 'bob', lastName: 'dobbs' ]

I'd like to create a dynamic property fullName that evaluates to "$lastName, $firstName".

While it sort of does work to do this:

dynamic.fullName = { "$lastName, $fullName" }

It requires an call() or implicit call with () to return the string, otherwise it just gives the closure toString()

assert dynamic.fullName() == 'dobbs, bob'

Passes

But

assert dynamic.fullName == 'dobbs, bob'

Fails because that evaluates to the toString of the closure

I can always do this

Dynamo dynamic = [ firstName: 'bob', lastName: 'dobbs', fullName: 'dobbs, bob' ]

But that ain't DRY...


Solution

  • For parameterless methods, Groovy needs parenthesis. I can think of two solutions:

    Solution 1: Metaclass the getter getFullName:

    Dynamo dynamo = [ firstName: 'bob', lastName: 'dobbs' ]
    
    dynamo.metaClass.getFullName = { "$lastName, $firstName" }
    
    assert dynamo.fullName == 'dobbs, bob'
    

    Solution 2: Hook into the property getters with getProperty:

    Dynamo dyn2 = [ firstName: 'john', lastName: 'doe' ]
    dyn2.metaClass.getProperty = { String property ->
        if (property == "fullName") { 
            "${delegate.lastName}, ${delegate.firstName}" 
        } 
    }
    
    assert dyn2.fullName == 'doe, john'