Groovy, let me count the ways in which I love thee
Oh the wonder and the joy. I’ve been fixing some issues and adding features to the Grails Functional Testing plugin (now informally known as The G-Func) and had cause to marvel at some Groovy treats.
The problem: HtmlUnit, which the plugin uses for HTTP interactions, has an extensive hierarchy of classes representing page and form elements. There is however no way to indentify some of these as specific functional groups eg there are 4 different button types, which have no interface or ancestor that is not also shared by some other non-button classes.
So a little ugly logic is needed. For example when the plugin’s form wrapper uses Groovy dynamic property resolution to get a form field for you, it is now doing something like this:
// Note this switch is polymorphic, order of cases is IMPORTANT
switch (f.class) {
case BUTTON_CLASSES:
case HtmlFileInput.class:
return f // return the field object itself, not its value
case HtmlSelect.class:
return f.selected
case HtmlRadioButtonInput.class:
return f.checked
case HtmlCheckBoxInput.class:
return f.checked
case HtmlTextArea.class:
case HtmlInput.class:
return f.value
default:
throw new RuntimeException("Don’t know how to get a value from form element of type [${f.class}]")
}
}
Now, what is special about this is:
- Groovy’s switch statement is very cool. It calls isCase(value) on the objects used in the case statements. That’s pretty basic Groovy stuff, but is still news to most Java developers.
- The use of BUTTON_CLASSES in the case is very cool. That property is a list of HtmlUnit classes that are “buttons” in conceptual terms. Groovy will call isCase on the list, which will return true if the list CONTAINS the switch value. That saved me 4 case lines and made my code more DRY as this check is also done elsewhere.
- The isCase implementation on Class added by the Groovy GDK is polymorphic. So the “case HtmlInput.class” actually catches any descendent of HtmlInput – of which there are several. This is also why the case BUTTON_CLASSES is at the top of the switch, as some of those classes extend HtmlInput and would be incorrectly caught by this HtmlInput clause.
Many people will no doubt say I can simplify the code above further – the “.class” are largely not necessary and I could merge the “return f.checked” cases. I may well do in future, but this is there for clarity at the moment
Recent Comments