A friend of mine sent me this link a while back on Higher Order Messaging in Ruby, which tracks back to this original post on HOM in Ruby and eventually tracks back to a presentation given on HOM in Objective-C.
After reading the original post I decided to try my hand at a HOM in Groovy. For me it was mostly a learning experience so that I could get my feet wet with attempting to write a DSL. And it was a great learning experience, unfortunately there are a few stumbling blocks for my implementation in the Groovy language. But without further ado he is my very rough stab at it (You are welcome to take this code and make something more of it, as I stated this was primarily a learning experience and I have no real desire at this point to take it further).
Here then is what I did, first with the test case:
void testHOM() {
def list = createCollection()
def newList = list.findAll{ boomer -> boomer.retired()}
assert newList.size() == 5
list = createCollection()
newList = list.where.retired()
assert newList.size() == 5
}
def createCollection() {
List boomers = [ new Claimant(name:"Joe", age:65, gender:"M"),
new Claimant(name:"Jan", age:61, gender:"F"),
new Claimant(name:"Bob", age:50, gender:"M"),
new Claimant(name:"Ana", age:60, gender:"F"),
new Claimant(name:"Tim", age:60, gender:"M"),
new Claimant(name:"May", age:65, gender:"F"),
new Claimant(name:"Fay", age:59, gender:"F"),
new Claimant(name:"Jay", age:70, gender:"M") ]
return boomers;
}
The first part simply shows that we are looking through all of the retired claimants, which is decided by this code in Claimant.groovy:
def boolean retired() {
return (((gender == "M") && (age >= 65)) || ((gender == "F") && (age >= 60)))
}
The second part is where we actually start using Higher Order Messaging. What is the idea behind HOM?
In short the idea is to provide a way to pass messages to objects (in this case “where”) and let the object deal with it (in this case by calling the retired() method on each item in the collection. Let’s take a look at how to actually do that. First we need to create a MetaClass that will intercept method calls (although not all method calls are intercepted which I have filed a bug on already). Here is the HigherOrderMessage object that will get returned when where is called.
package hom;
import org.codehaus.groovy.runtime.InvokerHelper
class HigherOrderMessage {
def object
def prop
def HigherOrderMessage(obj, prop) {
this.object = obj
this.prop = prop
}
def Object invokeMethod(String methodName, arguments) {
List list = InvokerHelper.asList(arguments);
println list
if(list.size() == 0) {
return this.object.findAll{item -> item."${methodName}"()}
}
return this.object.findAll{item -> item."${methodName}"(arguments)}
}
}
This helps us keep track of what object we are currently working on within a given HOM (in our case a collection of Claimants and it also helps us track what operation to perform on them, where in this case. As I said earlier, this is an extremely simple implementation, this could greatly be expanded to do a number of things and this implementation is by no means the most ideal (for instance you don’t want to keep track of where, you really should have a closure or something else, hopefully you get the idea).
As you can see by my poor code example, I’m not even checking to see what operation we’re performing, quite honestly I got fed up with some stuff and just wanted something that worked, so you get to see my poor code
. But essentially all this class does is call the actual method that is tacked on to the end of the HOM call list.where.retired() on each item in the collection using Groovy’s closure for findAll.
Next, is the actual part that intercepts the where property call and returns this object, ArrayListMetaClass.
package groovy.runtime.metaclass.java.util
import groovy.lang.DelegatingMetaClass
import groovy.lang.MetaClassRegistry
import org.codehaus.groovy.runtime.InvokerHelper
import hom.HigherOrderMessage
class ArrayListMetaClass extends DelegatingMetaClass {
def parent
def collection
def hom
def queryTypes = ['where', 'unless']
ArrayListMetaClass(final Class myClass) {
super(myClass)
initialize()
}
ArrayListMetaClass(MetaClassRegistry registry, final Class myClass) {
super(myClass)
}
def Object getProperty(Object obj, String prop) {
if(queryTypes.contains(prop)) {
this.hom = new HigherOrderMessage(obj, prop)
return this.hom
} else {
return super.getProperty(obj, prop);
}
}
}
This class intercepts all calls to ArrayList through some Groovy magic. First there is the name itself ArrayListMetaClass, and the second is the package it lives in groovy.runtime.metaclass.java.util (it actually took me a couple of tries to get this right, originally I put it in groovy.runtime.metaclass.java.lang which only works for classes in that package, so make sure you just prepend the groovy.runtime.metaclass to whatever Java package you need to drop it in, or your own package for that matter).
Once it has intercepted the where property call it then returns a HOM object that we saw earlier. This could of course be extended to embed a closure instead of a simple string to the HOM and then it would be extremely flexible.
All in all this was an extremely interesting exercise and got me much deeper into the Groovy internals and jazzed about the DSLs that I need to write (currently working on those, will post more as I get some code ready for sharing). If you are interested in my poor code, you can grab it here.
Related Posts: