Friday, February 09, 2007

Grails 0.4.1 out, Groovy just got more dynamic

Haven't posted in a while, but the focus for the last few weeks, Grails wise that is, has been getting 0.4 out and then putting out a sensible point release from community feedback. Grails 0.4.1 is that point release and is out now.

So now that things have settled down a bit I want to talk a little about one of the features in Grails 0.4 that really excites me. It is called the ExpandoMetaClass and what it does is bring to Groovy easy addition of dynamic features kind of like in Ruby's meta stuff and Javascript's prototype object.

Groovy has always had the underlying infrastructure to make magic happen, it has just been hard to get to for Joe user, particularly when coming from other languages with these features. In Grails, this is no longer the case and the plan is to move this functionality into Groovy core in the future. So how does it work in Grails?

Well say you have a class, it could be a Java or a Groovy class, and you want to add a method to it. Let's take java.lang.String for example. Say you wanted to add a new method that takes an existing String and swaps the casing of the String so upper case letters are in lower case and vica versa. This is how you do it:


String.metaClass.swapCase = {->
def sb = new StringBuffer()
delegate.each {
sb << (Character.isUpperCase(it as char) ? Character.toLowerCase(it as char) :
Character.toUpperCase(it as char))
}
sb.toString()
}


assert "UpAndDown" == "uPaNDdOWN".swapCase()


Job done. Notice the special use of the implicit "delegate" variable that equates to "this" inside the method. Now the nice thing is it is not just methods you can do this for. You can add constructors, properties, instance methods, and static methods (checkout the docs for more info) on any Java OR Groovy class. Hopefully this will help make Groovy Meta programming more accessible.

4 comments:

Anonymous said...

Looks great. What's the difference between this and Groovy Categories?
Milan

Graeme Rocher said...

There are a number of differences between this and categories. First this will change every class within the VM whilst categories only apply for the block which you use the use statement with:

use(MyCategory) {

}

Next there are limitations as to what you can do with a category in terms of adding contructors, static methods etc.

There are no such restrictions here.

Sam said...

As the original contributor of Categories I'm surprised that this is going in. The reason we didn't add it the first time was because in a multithreaded world that Ruby rarely ventures into this kind of thing is very unpredictable whereas Categories had well defined behavior.

Graeme Rocher said...

Hi Sam,

Indeed you are correct, and it will be disabled by default within Groovy.

However there are many circumstances where you ARE in control of threading and the environment. In this case it makes sense to offer a simpler way to add dynamic features.

Categories, although helpful, are counter-intuitive for many. With regards to Ruby and Javascript, these languages also have problems when it comes to dealing with threads (green threads in Ruby, real threads in JRuby) and concurrent modifications of meta classes (prototype object in JS)

However when used sensibly (ie you do all of the method registration at startup time) there is little issue. It seems there is a unfounded fear of this in Groovy land. Sure you can shoot yourself in the foot, but as I keep saying "With great power comes great responsibility" ;-)