Dynamic Methods in Groovy

I started working in Groovy and Grails about a month ago and, beside the fact I was already comfortable with the language and framework, yesterday I was especially pleased using some dynamic features offered by Groovy.

What I needed to do is to render some JSON objects in GSP page. Although this JSON structure was known, it did not belong to the application’s domain model. So,I started with something like this:

class Events {
    def getEventsHolder() {
        ...
        def data = JSON.parse(jsonText)
        data
    }
    ...

Obviously, code above parses JSON string and returns object representing this structure.This object worked nicely in GSP page and I was able to read and render object’s properties, access objects in collection fields, etc. However, soon I realized that I need to apply some formatting to fields and that I’ll need helper methods to test object’s state. Possible solutions from a top of my head were:

  • use <g:if test=”…”> for series of tests,

  • add formatting code in GSP page,

  • make a custom taglib,

  • make a wrapper class for JSON parsed objects

All this looked like a straight forward but tremendously boring thing to do. In addition, whichever path I take it would “pollute” my code more than I like. (Well… if I were using Java I would probably take one of the options above without much further thinking.) However, I was hoping there is a more elegant way to solve this in Groovy. After all, I’ve chosen Grails for this project to test its flexibility and advertised productivity gains so I thought it was time to learn and try out some of new things…

I recall reading before that in Groovy one can add his own methods to already existing classes (the same way Groovy it self extends Java core classes). So, instead to create a wrapper class and re-pack my JSON object structure every time I needed data, my idea was to dynamically add needed methods to generic class used by JSON parser. I read ExpandoMetaClass with few related pages and not much later I came up with the following solution:

class Events {
    def getEventsHolder() {
        ...
        def data = JSON.parse(jsonText)
        expandEventClass(data.events?.get(0))
        data
    }
    def expandEventClass(obj) {
        if(!obj)
            return
        def meta = obj.getClass().metaClass
        if(meta.hasMetaMethod('myGetCommentsFormatted'))
            return
        meta.myGetCommentsFormatted = { -&gt;
            // implement method here...
        }
    }
    ...

You can see that after parsing the JSON text I’m inviting method expandEventClass passing the first object from events collection (line 5). In the method I take class of received object (in my case that class is coming from JSON parser) and then it’s metaClass (line 11). Once you add a dynamic method to the class it applies to all instances so I check if metaClass already has method “myGetCommentsFormatted” defined (line 12) and if it does I exit. Otherwise, I’m adding “myGetCommentsFormatted” method to the meta class (line 14). After this I can use this method and reference it in GSP as any other method or property:

<div class="comment">${event.myGetCommentsFormatted()}</div>

Comments