Alright, so I lied. I decided to use this example elsewhere and definitely needed something more flexible. So I spent a couple of hours (and one weird Groovy compilation bug later) and got my HOM implementation so that it uses closures to find stuff. It still uses findAll, but now it makes the determination from the closure passed into it. Now, theoretically you can implement any “message” you want and associate it with a closure in ArrayListMetaClass and voila you have a new operator for any collection. Of course HOM should be more flexible as well, but that I will leave up to others as far as implementation (unless the bug bites me again and I want to expand it a bit more).
Here then is the new relevant code for ArrayListMetaClass:
def whereClosure = { method, args, item ->
List list = InvokerHelper.asList(args);
if(list.size() == 0) {
if( item."${method}"() ) {
return item
}
} else {
if( item."${method}"(args) ) {
return item
}
}
}
def unlessClosure = { method, args, item ->
List list = InvokerHelper.asList(args);
if(list.size() == 0) {
if(!item."${method}"() ) {
return item
}
} else {
if(!item."${method}"(args) ) {
return item
}
}
}
def queryTypes = ['where': whereClosure, 'unless': unlessClosure]
…
def Object getProperty(Object obj, String prop) {
if(queryTypes.containsKey(prop)) {
this.hom = new HigherOrderMessage(obj, queryTypes.get(prop))
return this.hom
} else {
return super.getProperty(obj, prop);
}
}
So, the new items that you’ll notice here is that I decided to go with the closure idea and created two new closures. The whereClosure takes three arguments, the method we’re calling, the arguments to that method and the actual item that needs to get called. Here’s what happens when this closure gets passed in to the HOM object:
def Object invokeMethod(String methodName, arguments) {
def closure = this.prop.curry(methodName, arguments)
return this.object.findAll(closure)
}
What we are doing is currying the closure that was passed in and setting the method name and arguments (as they are passed in to invokeMethod) and then passes the curried closure into the findAll() method. Of course I’ve added in a new unit test as well to test my new unlessClosure.
newList = list.unless.retired()
assert newList.size() == 3
assert newList == nonRetiredCollection()
Again, if you want the code, you can grab it here