biniam
biniam

Reputation: 8199

Groovy metaclass in Grails

I added a behaviour for Map of Lists (a map where the key is one Integer but the values are List) in Grails. If the key doesnot exist, I will insert the Integer key and one value. But if the key exists, I want to concatenate values as a List /rather than replacing the existing value.

/**
* E.g.
*  [1: [123, 456, 789], [2: [987, 654, 321]]]
*/
LinkedHashMap.metaClass.multiPut << { key, value ->
    delegate[key] = delegate[key] ?: [];
    delegate[key] += value
}

My question is: Where shall I put this in Grails? in Bootstrap.groovy's init or as AST Transformation (if so, how?)?

Upvotes: 0

Views: 328

Answers (2)

defectus
defectus

Reputation: 1987

I'd personally go for a Groovy extension module. These are loaded right during the Grails init phase so can be used pretty much anytime over the app lifetime.

More on these extensions can be found here: http://mrhaki.blogspot.cz/2013/01/groovy-goodness-adding-extra-methods.html

The module loading support has been added to Grails as per this Jira ticket: https://github.com/grails/grails-core/issues/1307

On the other hand I don't see anything wrong with using bootstrap either.

Upvotes: 1

injecteer
injecteer

Reputation: 20699

Looking at your "new" method I can't stop thinking about the already existing one:

def map = [:].withDefault{ [] }
assert map[ 'key' ] == []
map[ 'key' ] << 'value'
assert map[ 'key' ] == [ 'value' ]

The thing is, that concatenation has nothing to do with the Map, which contract takes care only about adding or replacing of values.

Here the possible problem is the null-value. if you do map.someKey += 'aaa', you get a NPE, if the key doesn't exist. To make sure, that the values never turn null, at least upon the 1st call. This is where withDefault() come in handy.

In my example above I initialized the map with default values of [], meaning that if the key doesn't exist, the new key gets inserted with the value of [].

You could swap the [] with a '' or any other type which allows concatenation:

def map = [:].withDefault{ "" }
map.newKey += 'aaa'

def map = [:].withDefault{ 0 }
map.newKey += 10

Upvotes: 0

Related Questions