Friday, September 21, 2007

The new meta-programming APIs in Groovy 1.1 beta 3

As announced by Guillaume, Groovy 1.1 beta 3 is out and the final release is just round the corner. There is lots of good stuff in there, but I want to talk here primarily about the new meta-programming API improvements that have been introduced in this release.

Groovy has always had the underlying Meta Object Protocol (MOP) that allowed the same behaviour as languages such as Ruby and Small Talk, however the API onto these has, up until now, not been as elegant as it could have been. With Groovy 1.1 beta 3 there is a lot more at your finger tips including...

Runtime evaluation with respondsTo & hasProperty
It is now a lot easier to inspect the runtime thanks to the addition of hasProperty and respondsTo for meta classes so you can do:


class Foo {
String name = "Wilma"
def sayHello() { println "hello $name" }
def sayHello(String message) { println "hello $message" }
}
def f = new Foo()

if(f.metaClass.respondsTo(f,'sayHello') {
f.sayHello()
}


Since Groovy supports method overloading and typed arguments to support good Java interoperability you can also specify a type:


if(f.metaClass.respondsTo(f,'sayHello', String) {
f.sayHello("Fred")
}

Groovy also differentiates method and property access so you can do this:

if(f.metaClass.hasProperty(f,'name') {
println f.name
}



See the docs here for more info on hasProperty and respondsTo

Missing method/property interception with methodMissing & propertyMissing

As of beta 3, Groovy now supports the concept of "method missing". It has always been possible to intercept method dispatch using invokeMethod. However, this has the overhead of intercepting every method call instead of just the "missing" ones. As of Groovy 1.1 beta 3, Groovy now supports "method missing" and "property missing", which are only called when method dispatch fails (ie just before a MissingMethodException would be thrown anyway.)

A trivial example can be seen below, we use this feature in Grails to implement dynamic finders such as findByTitleAndAuthor("Groovy in Action", "Dierk Koenig"):


Foo.metaClass.methodMissing = { String name, args ->
Foo.metaClass."$name" = { Object[] varArgs ->
"$name : ${varArgs.inspect()}"
}
delegate."$name"(args)
}

def f = new Foo()
f.sayHello('Fred')
assert f.notARealMethod("boo")
== 'notARealMethod : [["boo"]]'
assert f.notARealMethod("boo", "hoo")
== 'notARealMethod : [["boo", "hoo"]]'


Notice how in the example above we can dynamically register a new method on the fly and then call that method. This also has the implicationthat the next dispatch to the same method is faster. See the docs here for more info and there are a whole load of docs on doing dynamic Groovy and meta-programming to be found here.

6 comments:

Anonymous said...

almost as cool as Ruby :-)

seriously: great stuff. Groovy is getting better and better.

Claus

Anonymous said...

Way to go Graemer! hasProperty() is a feature I was expecting for a quite a while. These changes surelly make Grails development a lot easier ;-)

Anonymous said...

It is strange, that this functions was missed previously.

Peter said...

Hi Graeme,

Is there also a way to find out the properties of a class without having to create an instance? For example:

class User {
String firstname, lastname
}

println User.metaClass.properties

Also is there some way to 'identify' a property for example User.@firstname?

In Ruby I saw this example of binding:

model = Person.new('Julian Doherty', 'julian@example.com')

name_label = JLabel.new
name_label.bind_to model, :name

It would be cool if the Groovy SwingBuilder binding could work this way. The current binding examples I found bind components together I haven't found any model binding examples. Do you know of any plans to work on this?

Good luck on Groovy/Grails,

Peter

Graeme Rocher said...

Hi Peter

Your example

println User.metaClass.properties

Will work, the only thing to be aware of is that it returns a list of MetaProperty instances. So for example you can do this:

User.metaClass.properties.each { println p.name }

As for your binding example I know there is a lot of activity going on around SwingBuilder and bindng. Checkout Danno's blog for the info:

http://shemnon.com/speling/

Peter said...

Thanks Graeme indeed User.metaClass.properties works in the latest Groovy version :)

But if you need the type of firstname property I think you need to do:

User.metaClass.properties.grep { it.name == 'firstname'}[0].type

I think it would be a lot 'groovier' if you could do something like:

User.@firstname.type

Maybe I'm wrong and maybe I would better post this in the Groovy mailing list?

Best regards,

Peter