Thursday, 19 February 2009
Hitlers nightly build fails
Wednesday, 11 February 2009
Case-insensitive ordering using HSQLDB
Postgres was ignoring the case of items, whereas HSQLDB was not - so if I were to order the strings "Al" and "AM", HSQLDB would show "AM" before "Al", but Postgres would show "Al" before "AM".
Given that the behaviour of Postgres (case-insensitive ordering) was the desired behaviour, we wanted to get HSQLDB to do the same. After a lot of searching any many dead ends, we found that you simply use ".ignoreCase()" after an order criteria:
List books = Book.withCriteria() {
ilike('authorName', "%${searchKey}%")
order('authorName', 'asc').ignoreCase()
maxResults(max)
}
Immutable Objects in Groovy
Anyone familiar with Joshua Bloch's book Effective Java will know about the importance of immutable objects. However, writing classes like that in Groovy always seemed to me to be problematic. You can declare the properties final but if the property type is itself mutable encapsulation is broken. Defining properties as private wasn't enough as Groovy lets you access them from anywhere regardless (I'm still not entirely clear why the language even supports it). A combination of final properties with overridden get methods where property types are mutable works but then you're writing almost as much code as you'd need to in Java and who knows what someone can get away with by using the .@ operator!
Google provided the solution this morning. It turns out there is an @Immutable annotation in Groovy that solves the problem in a very neat way even giving you an implementation of equals, hashCode and toString for free. Those methods tend to be pretty much templated in immutable classes anyway so getting effective implementations without writing any code makes a lot of sense and feels very 'Groovy'.
Tuesday, 10 February 2009
Binding to collection fields on command objects
One of the cool new features of Grails 1.1 is the ability to bind form elements to collection properties of domain objects. Unfortunately it doesn't quite work out of the box for command objects.
Suppose I have a command class like this:
class MyCommand {
Map things = [:]
}
and my form posts params like this:
things[key1] = value1
things[key2] = value2
things[key3] = value3
I'd hope that the binder could cope with this and populate my command object's things property with the appropriate keys and values. It doesn't work as it seems that Grails attempts to figure out what the type of the object contained in the collection is. For domain objects this is possible since the hasMany
closure can be used. Command objects don't have this available and so you end up with a nasty stack trace boiling down to a NullPointerException
.
However there is a way to 'fool' the data binder. All it's trying to do is figure out the value type for the Map so it can use an appropriate PropertyEditor to decode the HTTP request parameter value. In this example the value is just a String. Initializing the Map like this provides a workaround:
import org.apache.commons.collections.MapUtils
import org.apache.commons.collections.FactoryUtils
class MyCommand {
Map things = MapUtils.lazyMap([:], FactoryUtils.constantFactory(''))
}
That's using a LazyMap
from Commons Collections which (in case you can't guess) is a Map implementation that populates a default value when get is called for an unmapped key. The Map in the command object will use the empty string as its default value which is enough for the Grails data binder to figure out the value type and bind our parameters.
I haven't tried the same trick with richer data types yet (e.g. a collection of domain objects in a command) but I don't think there's any reason why a similar workaround returning a different value from the Factory shouldn't work.
Sunday, 1 February 2009
Testing Url Mappings
def grailsUrlMappingsHolderBean
void testMappingForPersonController() {
def mappingInfo = grailsUrlMappingsHolderBean.match('/person/pain')
assertEquals 'person', mappingInfo.controllerName
assertEquals 'show', mappingInfo.actionName
assertEquals 'pain', mappingInfo.parameters.id
}
The grails testing plugin provides custom assertions for doing this and more (assertUrlMapping etc.).
kthxbye