Groovy #6 – Curious case of pedantic ignorance

If you know C, its easy to pickup C++.
If you know C++, its easy to pickup Java.
If you know Java, its easy to pickup Groovy.

We have all heard this. Yeah its easy to pickup something. But how easy it is to fit into it? Despite Java being touted as oo language, programmers still write a lot of procedural-type code in it. It is easy to ‘learn’ a language, but much more not easy to assimilate the philosophy of the language or familiarize with its idiosyncracies. I’m still somewhat finding difficulty in fitting into the philosophy of C#/.Net, where the Rate of Entropy (ie the time taken for the code to get into a disorderly or unorganized state) is way lower than that of Java.

When you realize that Groovy treats configuration files as yet another Groovy script, the concept of .properties is thrown out of the window. Along with that key=value pair, the .xml configs are also given a boot. Groovy allows a more compact, more predictable, less verbose, and a much more powerful way of handling configurations.

Consider the following snippet:

//Configuration.groovy
Eye = {
colors = ['black', 'blue', 'brown', 'green', 'gray']
}

//Person.groovy
class Person {
def config = new ConfigSlurper().parse(Eye)

boolean allowedEyeColor(def color) { color in config.Eye.colors }
}

//Test.groovy
Person p = new Person()
assert true == p.allowedEyeColor('black')

Ok, assuming that this is all the code, what is the most glaring mistake here?

a) In Person, parsing Eye is incorrect
b) Code will not work because allowedEyeColor is not a public method
c) Assert does not fail, the programmer is cheating
d) Assert should not have failed, the groovy compiler is cheating

If you had already spotted the bug, good for you. But you get bonus points if you say that Configuration.groovy is not unit tested 🙂

Now lets add a unit test case to directly test the Eye configuration and see the value of Eye.colors:

def config = new ConfigSlurper().parse(Eye)
assert Eye.colors.size() == 5
//Result: assert fails!

The problem had me scratching my head for a few minutes, until I realized that the Orwellian theory applies to programming languages as well – All languages are equal. Some are more equal than others.

Literally more equal, yes.

The Configuration.groovy has the following line:

Eye = {
colors = ['black', 'blue', 'brown', 'green', 'gray']
}

Perfectly normal for the Java-tinted eyes.

But in Groovy, the above code is actually declaring a closure. In order to make it a “property”, it has to be declared like this:

Eye {
colors = ['black', 'blue', 'brown', 'green', 'gray']
}

Now, this is actually a config dsl, and Eye.colors can be accessed as expected.

But in the closure case, it has to be accessed like a method:

assert config.Eye().colors.size() == 5

The equals sign had creeped into the code while typing. Call it a habit, reflex or a case of pedantic ignorance.

Advertisements

Groovy #5 – Property reflection by map notation

Yet another nice feature of Groovy is the ability to access a member of an object instance using map notation. This may look like a syntactic sugar at first, but the sugar coat leads to a nice icing on the cake.

Consider a class

class KeyValuePair {
String name
String value
}

The usual notation to access the members would be k.name, k.value. But in Groovy, you can also do it like the following:

KeyValuePair k = new KeyValuePair()
k['name'] = 'searchSite'
k['value'] = 'http://www.google.com'

assert k.name == 'searchSite'
assert k.value == 'http://www.google.com'

So, whats the big deal? The big deal is, reflection made ridiculously easy. Note that ‘name’ is actually a string that evaluates to the member name of the class. Which means it can be a variable.

Consider a trivial ORM implementation – a csv file with a header-row which represent column names and the rows which represent values. How will you generically convert that csv file into an array of a specific object?

class CsvDb {
List data = new ArrayList()

public List load(String fileName, String className) {
FileReader fr = new FileReader(fileName)
String header = fr.readLine()
if (header == null) return data

List columns = header.tokenize(",")
String line
while ((line = fr.readLine()) != null) {
def object = Class.forName(className).newInstance()
List row = line.tokenize(",")
columns.eachWithIndex { col, index -> object[columns[index]] = row[index]?:"" }
data.add(object)
}

data
}
}

Look at line #14, columns.eachWithIndex {} closure, it sets the value of the given object’s property using map notation to the value of the row in the corresponding column.

Now given any csv formatted data and a class that has members matching the column names, the above snippet will generate a List of the given objects.

/** c:/data/flix.csv **/
actor,movie,genre
Ron Livingston,Office Space,Comedy
Joe Pesci,My Cousin Vinny,Comedy
Daniel Craig,Skyfall,Action
Jamie Bell,Tintin,Animation

and we want that mapped to an object called Flix.

package com.moviedb

class Flix {
String actor
String movie
String genre

static List flix

static void load() {
flix = new TextDb().load("c:/data/flix.csv", "com.moviedb.Flix")
}
}