Monday, April 6, 2009

Using Groovy @Delegate

Groovy 1.6 introduces some killer stuff!

I really like @Delegate.

For those that don't use Groovy much, what @Delegate does is best seen by an example. The one in the link above is pretty good dealing with Date in an Event, but as a real world example here's how I needed it....

groovy.sql is pretty cool and you can call toRowResult() on each groovy result set object, which lets you use those GroovyRowResult objects directly calling the column name as properties... for example..


def sql = Sql.newInstance("jdbc:jtds:sybase://.....I", "user", "passr", "net.sourceforge.jtds.jdbc.Driver")
def results = []
sql.eachRow("SELECT firstName, lastName FROM Person") {
results << it.toRowResult()
}


//print first and last names from results collected:
results.each {
println "firstName = $it.firstName lastName= $it.lastName"
}


Ok simple enough, but now what if you wanted to also get a "fullName" property? Ok simple enough we'll just make a Person object and create a method to create it. But wait, I still need access to all those other database fields. Now sure in many CRUD apps you'd already have a Person object with all the typical fields firstName, lastName etc... but in this case I only need display data and I'm perfectly happy with using the database field names for the property names so I want to just use my groovyRowResult directly but also want to be able to call "lastNam.". So in Java what would one do? I don't want to mess around trying to extend GroovyRowResult (if I even could?) or maybe my Person also needed to already extend another Base object as it is. Obviously you want to wrap row in an adapter and then in Java you'd have to use some reflection and proxy method/property calls to the wrapped row object if a call to a method/property wasn't found in Person. Sort of a pain to have to code that proxy stuff though. Granted not difficult and you can cut and paste it from other projects once you've done it once, but with Groovy it's even easier and much cleaner. for my sql example above...


class Person {
@Delegate(interfaces=false) GroovyRowResult row

def getFullName() {
return "${row.firstName} ${row.lastName}"
}
}

//...
sql.eachRow("SELECT firstName, lastName FROM Person") {
results << new Person( row: it.toRowResult() )
}

results.each {
println "firstName = $it.firstName" // finds firstName in delegate!!!!
println "lastName = $it.lastName"
}


Before using Delegate I was using propertyMissing, which would be fine in this case since I only have one delegate but things would be trickier if I had multiple nested objects (whereas using @Delegate it wouldn't be an issue - assuming you didn't have conflicting method/property names - in which case not sure what would happen.) Even using the old propertMissing in Groovy is quite cool:


//using propertyMissing
class Person {
@Delegate GroovyRowResult row

def getFullName() {
return "${row.firstName} ${row.lastName}"
}

def propertyMissing(String prop) {
def result = row."${prop}"
}
}

No comments :

Post a Comment

Blogger Syntax Highliter