Sunday, May 20, 2007

Could it be any easier?

Well here I am again, on the second week of development of my Grails based 'addictive time waster' web2.0 site (I'm not rushing and spend may be an hour a day on it). As I described in the previous post, I was having a blast with Grails custom codecs feature which enables adding various 'encoding/transformation' features to any arbitrary Java types with ease! Well, I'm continuing "abusing" custom codecs with a great level of success and most importantly with a lot of fun.

As I noticed on almost all of those user generated content web2.0 style sites, there are some kind of a 'Submitted by: user234 x days ago type of a piece of info. So, I've decided to add 'time ago' type of a stat to my web app. I'll continue to show the 'Submission' class for the examples, representing user's submission of any kind. So in order to track when Submissions are created, I've added a 'createdOn' property:


class Submission {

...
Date createdOn = new Date()
...
}


That will take care of recording the date of submission. Now, when retrieving and showing the the submissions, there would need to be some kind of a calculation to show the 'x minutes/hours/days/weeks/months/years ago' info. So, I've decided to "abuse" the codecs feature and "attach" the "encoding" of the date type into the 'time ago' string to the java.util.Date type. Thanks to the "richness" of the Joda time library, I was able to do that easily and put the code into the custom codec:


import org.joda.time.*

class TimeAgoStringCodec {

static encode = { date ->
def start = new DateTime(date)
def end = new DateTime(new Date())
def ago = Minutes.minutesBetween(start, end).getMinutes()
if(ago == 0) {
return 'less then 1 minute ago'
}
else if(ago < 60) {
return "$ago minute" + (ago > 1 ? "s ago" : " ago")
}

ago = Hours.hoursBetween(start, end).getHours()
if(ago < 24) {
return "$ago hour" + (ago > 1 ? "s ago" : " ago")
}
ago = Days.daysBetween(start, end).getDays()
if(ago < 7) {
return "$ago day" + (ago > 1 ? "s ago" : " ago")
}
ago = Weeks.weeksBetween(start, end).getWeeks()
if(ago < 4) {
return "$ago week" + (ago > 1 ? "s ago" : " ago")
}
ago = Months.monthsBetween(start, end).getMonths()
if(ago < 12) {
return "$ago month" + (ago > 1 ? "s ago" : " ago")
}
ago = Years.yearsBetween(start, end).getYears()
return "$ago year" + (ago > 1 ? "s ago" : " ago")
}
}


And equipped with this codec, I am then able to call: submission.createdOn.encodeAsTimeAgoString() and viola, I have the proper 'time ago' for any Date type application-wide!

Here I could conclude that adding 'codecs' to any arbitrary Java type application-wide is piece of cake in Grails, and also here is a great example of the advantage of Groovy's seamless integration with "raw Java" where I was able to make use of an existing Java library (Joda time) with zero integration overhead.

Later...

2 comments:

jayshao said...

why plug it into the model, and not have this be some kind of decorator which is applied by the view?

Dmitriy Kopylenko said...

You could do it as a GSP dynamic tag lib as easily in Grails. I chose to have it in the model as it aligns more with DDD, this logic becomes reusable elsewhere in the application, and in general it's an idiom that Grails promotes - domain centric design.