Okay, maybe that’s an exaggeration, but I’ve been digging into Groovy in Action (GinA) and the Definitive Guide to Grails (DGG) more lately and keep finding nuggets that I apparently missed on my first few readings.
(I’m trying not to be annoyed about that, btw. By this stage in my career, I know all too well that I have to re-read things over and over to really “get” them. I just wish I retained more than I apparently do. I guess I’ll always think that.)
Anyway, recently I wrote a blog post about how I was able to iterate over Date
s because Groovy treated them as a Range
(yes, capital R). I thought that was really cool and wasn’t sure exactly why it worked.
Lo and behold, on page 96 of GinA, Listing 4.1 includes
def today = new Date()
def yesterday = today - 1
assert (yesterday..today).size() == 2
Page 98 points out that “any datatype can be used with ranges,” provided it implements next()
and previous()
and implements Comparable
. This is equivalent to overriding ++, –, and the good ol’ spaceship operator, <=>.
(As a long-time Java developer, I have to say that any language that has a spaceship operator is inherently cool. Except Perl, of course. :))
I keep finding ways to rewrite my code as more idiomatic Groovy (or maybe I should just say, groovier). I used to calculate the number of days in a training course from the start and end dates using:
int days = 0
for (date in startDate..endDate) { days++ }
Now I realize that since a Range
is an actual object, it’s simpler just to write:
int days = (startDate..endDate).size()
At least when I wrote the first version, I had a feeling I was making it too complicated.
Moving on to Grails, I wanted to have a “days” property on my Course
class, but it’s a dependent property. Its value is determined by the existing startDate
and endDate
properties. That meant I didn’t want to add it as a formal property, because that would imply the existence of a setter method as well as a getter. Also, it doesn’t exist in the database and doesn’t need to.
I couldn’t find an appropriate annotation in DGG, but a quick question on the mailing list cleared it up. My class now looks something like
class Course {
int id
Date startDate
Date endDate
String title
// associations, toString() override, and constraints as usual
transients = ['days']
int getDays() { return (startDate..endDate).size() }
}
By providing the getter, I don’t need the attribute, which is a normal JavaBeans convention. By putting the property in the transients
closure, I’m telling Grails that it doesn’t have a corresponding column in the database.
Unfortunately, I found that when I tried using “days” as a property in in a GSP, sorting on it didn’t work. I think that’s because the tag actually generates a database query with an
order by
clause, and that’s going to be a problem with a property that doesn’t exist in the database.
I did realize I could compute my total number of training days in a one-liner:
Course.list()*.days.sum()
That uses the wicked cool spread operator, too. In my CourseStatistics
class I don’t actually make that list query more than once, but it’s nice to know I could.
I’ve been using Groovy and Grails for about a year now, and it’s nice to know they’re starting to sink in. 🙂
Leave a Reply