Categories
Grails

Silly GORM tricks, part I: Lists

In GORM, when one class has a hasMany relationship with another, a java.util.Set is injected into the class. Sometimes, though, I want to use a List instead in order to maintain ordering. The Grails reference documents (see section 5.2.4 specifically) discuss how to do that, but there are other issues that I needed to solve in order to make this work.

Consider an application that demonstrates the issue involved. It has only two domain classes, Quest and Task. A Quest consists of many Tasks.


class Quest {
    String name

    static hasMany = [tasks:Task]

    String toString() { name }
}

class Task {
    String name

    static belongsTo = [quest:Quest]

    String toString() { name }
}

I’m using a bi-directional one-to-many association here, mostly because the dynamic scaffolding works well with it (not the best reason, of course, but it makes it easy to illustrate the point). The hasMany assignment means that the Quest class will have a Set injected into it called tasks, and the belongsTo relationship means that all the cascade relationships (save, update, and delete) will work, too.

Before I take advantage of that in my boostrap code, though, I used the dynamic scaffolding just to make sure I could add quests and tasks through the normal views.

class QuestController { def scaffold = Quest }

class TaskController { def scaffold = Task }

As it happens, everything does work as advertised. A simple integration test that demonstrates it is shown below, which works.


void testAddTasks() {
    Quest q = new Quest(name:'Seek the grail')
    q.addToTasks(name:'Join King Arthur')
        .addToTasks(name:'Defeat Knights Who Say Ni')
        .addToTasks(name:'Fight Killer Rabbit')
        .save()
    assertEquals 3, Task.count()
}

Everything so far is standard stuff. One of the defining characteristics of a Set, however, is that it does not support ordering. If I want ordering, there’s a chrysalis stage I can go through on the way to a List, which is to use a SortedSet (assuming Task implements the Comparable interface).


class Quest {
    String name
    SortedSet tasks

    static hasMany = [tasks:Task]

    String toString() { name }
}

class Task implements Comparable {
    String name

    static belongsTo = [quest:Quest]

    String toString() { name }

    int compareTo(Object o) { 
        return name.compareTo(o.name) 
    }
}

The dynamic scaffolding still works, too. I can add a task, as long as there is a quest available to add it to. The tasks are sorted by name, as they should be. I added the above quest and tasks to my bootstrap code, too, so they were available as soon as my server started.

Incidentally, there’s a down side to using a SortedSet that I hadn’t realized right away. When I first wrote my application, I added a degree of difficulty to my tasks and tried sorting by them.


class Task implements Comparable {
    String name
    Integer difficulty

    // ...

    int compareTo(Object o) { 
        return difficulty - o.difficulty 
    }
}

That sorts tasks by difficulty all right, but there’s another consequence. I can only add a single task of a given difficulty to a particular quest! I can’t have two tasks both with the same difficulty. A SortedSet may be sorted, but it’s still a set. 🙂

So now I move on to using a List. As the reference documentation says, to do that, just declare tasks to be a reference of type List.


class Quest {
    String name
    List tasks

    // ...

    static hasMany = [tasks:Task]
}

Now there’s trouble. The server starts, and the bootstrap code works, because it adds tasks to an existing quest before saving them. The dynamic scaffolding has a serious problem, though. When I go to the tasks list and try to add a new task, everything is fine until I try to save the new task.

If I try to add a new task through the “Create Task” page, I get an exception: “org.hibernate.PropertyValueException: not-null property references a null or transient value“.

The reason is addressed in the reference documentation. First, changing to a list means that the database table for tasks now has an index column. Second, as the documentation says, you can’t save a task by itself any more — you have to add it to a quest first. It’s okay to say:

def t = new Task('Answer the bridgekeeper')

but I can’t save it by itself, or that index column will be a problem. I have to add the task to a quest first before saving.

Quest.get(1).addToTasks(t).save()

That works. Otherwise I get a null in that index column, which throws an exception and down goes the server.

So, knowing that, how do I fix the system?

Well, I definitely have to abandon the dynamic scaffolding. The built-in save method isn’t going to work, because it saves the task independently of the quest. So, it’s time to generate the real controllers.

After generating the task controller and views, the save method looks like:


def save = {
    def task = new Task(params)
    if(!task.hasErrors() && task.save()) {
        flash.message = "Task ${task.id} created"
        redirect(action:show,id:task.id)
    }
    else {
        render(view:'create',model:[task:task])
    }
}

I need to add the task to a quest and then save the quest. Fortunately, one of the parameters in the request is the id of the quest, under params.quest.id. That means my first try is to change the above code to this:


def save = {
    def task = new Task(params)
    def q = Quest.get(params.quest.id)
    q.addToTasks(task)
    if (!task.hasErrors() && q.save()) {
        // ... etc ...
}

Unfortunately, this doesn’t work either. When I fill in the page to make a new task and try to save it, I get a NullPointerException due to the fact that the task still has a null id.

This, I believe, turns out to be a Hibernate problem. Hibernate doesn’t do the save when I ask it to, but rather is waiting until the “right moment” to do the commit. Unfortunately, I need that commit right away.

Fortunately, there’s an answer to that, too. The save method takes a flush parameter which can be set to true.

Therefore, I changed the above code to:


def save = {
    def task = new Task(params)
    def q = Quest.get(params.quest.id)
    q.addToTasks(task)
    if (!task.hasErrors() && q.save(flush:true)) {
        // ... etc ...
}

Now, at long last, it all works. The key was to add the task to the quest and save the quest with flush set to true.

It’s possible that there are alternative solutions, but this one worked for me. If you know of better alternatives, please let me know.

Categories
Grails

Some notes about the Windows installer for Grails

This isn’t really a problem, but I don’t think it’s documented anywhere, so I thought I’d record it here.

(And by the way, if your reaction to my Windows-based comments is going to be “why not use something other than Windows,” my answer is (1) at least one of my machines is always running Windows, but more importantly (2) my clients overwhelmingly use Windows in their training rooms, so I can’t get away from it even if I wanted to.)

Like Groovy, the Grails downloads page includes a special installer for Windows. You don’t have to use it — it’s still fine to just download the zip file, unpack it, set the GRAILS_HOME variable and go on, but if you do decide to use the Windows installer you should know that a few details have changed.

First of all, when the installer runs, it creates a different directory structure than that found in the regular zip file. The installer creates a structure like:

c:\grails-1.0.2\
bin\
gbin\
grails\
… other files ..

which means the GRAILS_HOME variable needs to point to “c:\grails-1.0.2\grails” rather than “c:\grails-1.0.2” as the User Guide says. That also means that if you want to put Grails in your path for all command prompts, you need the “bin” directory under “grails”, not the one under “grails-1.0.2”. Personally, I assigned GRAILS_HOME to “c:\grails-1.0.2\grails” and put “%GRAILS_HOME%\bin” in my path, just be sure.

This also comes up if you’re using the JetGroovy plug-in for IntelliJ IDEA. The plug-in requires you to specify where the root of the Grails distribution is, which again is the “grails” subdirectory.

In principle, a lot of this isn’t even necessary. The installer creates a desktop shortcut called “Grails Environment” which is a configured command prompt. When I fire it up on my machine and check the path, I see that the directories

c:\jdk1.6.0\bin
C:\GRAILS~1.2\grails\ant\bin
C:\GRAILS~1.2\grails\bin
C:\GRAILS~1.2\GRAILS\..\gbin

have all been prepended (i.e., they appear at the beginning, rather than the end) to my path. The first one is very likely just my “%JAVA_HOME%\bin”, which was already in there, but again that’s not really a problem.

The other interesting characteristic about the “Grails Environment” window is that inside it, you don’t need to type the word “grails” in front of each command. You can just type “create-domain-class myclass” or whatever, and it works automatically. The Grails command line interface is already running.

For the create-* commands to work correctly, of course, you need to be in the root of your Grails application. The environment prompt looks like this:

[C:\grails-1.0.2]
grails:\>

at it starts, which is in the root of the Grails distribution. I needed to change directories to my own application, so after a couple of “cd”s I wound up with

[C:\grails_apps\myapp]
grails:\>

The environment supports all the normal DOS commands, like “dir” or “cd”.

Incidentally, there’s no problem firing up more than one instance of the “Grails Environment”. It creates a separate command window for each case.

There has also been a change to the documentation. In earlier versions, the User Guide (an excellent resource, getting better all the time) was stored in HTML form, and was basically a copy of the documentation found here. Now, using the installer, the documentation has been bundled inside a Windows help file, which has the file extension *.chm.

Finally, the installer adds entries under “Start->All Programs->Grails 1.0.2”. Those entries are:

API Documentation (a link to the included JavaDocs for Grails)
Grails Environment (discussed above)
Grails References (a link to the Windows help file)
Grails Web site (a link to http:\\grails.org)
Uninstall Grails 1.0.2 (which does what it says)

As with most things in Grails, it’s very forgiving. You can use it, or not. If you prefer the older (or should I say, “classic”?) style, just download the zipped distribution and go from there. If you want the installer to do that extra work for you, that’s fine too. I think, though, that including a README file of some kind containing all this information might be helpful.

Still, I tend to like these sorts of installers. I spend more time running training classes than I do on my own systems doing development, so set-up is an ongoing challenge for me. I’m tempted to tell the person setting up a training room to just download and run the Windows installer and they’re done. I may do that for my upcoming Grails class, but I haven’t quite decided yet.

By the way, Groovy also comes with a Windows installer. I really like that one. It creates the same directory structure that the zip file contains, but it also offers to create a GROOVY_HOME variable and add its “bin” directory to either your path or the system path, installs a native environment (whatever that is, but it sounded good), and downloads and installs optional components like Scriptom and the Graphics environment. Using that one is a no-brainer for me, though I’ve found it to be a bit slow.

I hope this helps someone. The creator of the installer is Chanwit Kaewkasi, who is active on the Grails users list. He was kind enough to answer my questions about it there.

Categories
Grails

Integration tests of controllers in Grails

The documentation on doing integration tests of controllers is a bit thin. I had to ask on the mailing list about how to do some of the basics, so I thought I’d make a record of the results here.

First of all, Grails distinguishes between unit tests and integration tests, in that integration tests involve functionality from the rest of the Grails environment. Running an integration test is rather like using the Grails console. All the dynamic finders are available, as well as any other properties injected by Grails. In a unit test, all of those items would have to be mocked somehow in order to isolate the class under test.

Chapter 6 of DGG discusses testing. The unit testing section is still pretty good, and there’s a section on using Mocks and Stubs which is interesting but feels a bit dated. I’m really looking forward to seeing the next edition of the book. Rumor has it that that’s already in the works.

The cool part is that according to the wiki, when you run “grails test-app”, Grails automatically injects a MockHttpServletRequest, MockHttpServletResponse, and a MockHttpSession from Spring into each integration test. The details of those classes are found in the API documentation for Spring in the org.springframework.mock.web package.

The example given in the wiki page referenced above shows what to do if controller methods end with either a render or a redirect. It turns out that the test case can use the mocked response property to check the resulting value. The sample there looks like


class FooController {
    def text = { render "bar" }

    def someRedirect = { redirect(action:"bar") }
}

so that the test case is


class FooControllerTests extends GroovyTestCase {
    void testText() {
        def fc = new FooController()
        fc.text()
        assertEquals "bar", fc.response.contentAsString
    }

    void testSomeRedirect() {
        def fc = new FooController()
        fc.someRedirect
        assertEquals "/foo/bar", fc.response.redirectedUrl
    }
}

Those are both interesting, of course, and it’s good that they point to the getContentAsString() and getRedirectedUrl() methods in the MockHttpServletResponse classes. My problem is that since both controller methods are invoked without input parameters, neither example shows how to set them.

Here’s the situation I was trying to test, reduced to trivial form. I have a Message class:


class Message {
    String sender
    String msg
}

The generated controller has (among other things) a create action that looks like


class MessageController {
    // ... lots of other actions ...
    def create = {
        def message = new Message()
        message.properties = params
        return ['message':message]
    }
}

So to test the create action, I need to send in some values in the params map.

To make a long story short (too late, I know, but so be it), there are two ways to do it. Here’s the first one:


class MessageControllerTests extends GroovyTestCase {
    void setUp() {
        new Message(sender:'me',msg:'test').save()
    }

    void testCreate() {
        def m = Message.findBySender('me')
        def mc = new MessageController()
        mc.request.parameters = [sender:'me',msg:'test']
        def message = mc.create().message
        assertToString message, "$m"
    }

    void tearDown() {
        Message.findBySender('me').delete()
    }
}

I needed to create a new message in setUp and save it, so that it would be accessible in my test. At the end I disposed of the message in the tearDown method.

In testCreate, I set the request parameters by invoking the setParameters(Map) method of MockHttpServletRequest. That took me a while to realize — I kept trying to set a “params” variable, since that’s what exists in the controller itself. Of course, from the request point of view, the map is a collection of parameters, not params.

The other approach is to deal with the params map directly by replacing the mc.request.parameters line above with


mc.params.sender = 'me'
mc.params.msg = 'test'

That works too and does essentially the same thing. I think I prefer the setParameters version, because I can see exactly what method is being invoked that way. Still, the difference is probably just a question of style.

(My thanks to Chris Chen, ckchris@idream.net, and Burt Beckwith, burt@burtbeckwith.com, for answering my question on the Grails user’s list.)

Finally, back in March of 2007, Glen Smith (of course) wrote up a nice blog post on unit testing controllers rather than doing integration testing. That relies on mocking the request, response, session, etc. with closures. While it demonstrates an interesting usage of metaprogramming to handle all the mocks, it feels like a lot of overhead to put in for each controller. Graeme himself commented on that and suggested developing a withMockController method for GroovyTestCase that did all the overhead for you. I don’t know if that ever actually happened or not, but it looks promising.

Since the whole Grails environment doesn’t have to be bootstrapped, unit testing with those mocks is inevitably going to be faster.

At the moment, I’m quite content to do integration tests, because I’m partly doing the tests to see how the environment works.

I sometimes feel that writing tests is a lot like brushing your teeth. Dentists will tell you that there are better ways and worse ways to brush, but in all honesty, if you just do it at all you’re way ahead of the game. I’m going to try and follow that philosophy and not worry about doing tests perfectly as long as I’m doing them at all.

Categories
Grails Groovy

A few 2GX notes

Late last night I returned home from the Groovy/Grails Experience (2GX) in Reston, VA.  I met many wonderful people and learned tons of new things, which I’m sure will spawn blog posts over the next few weeks.

Just to get started, though, I thought I’d mention a few random observations from the conference.

  1. Buy Scott Davis’s Groovy Recipes book!
  2. All of the major players I met from the Groovy and Grails projects (Dierk Koenig, Graeme Rocher, Jeff Brown, Jason Rudoph, and many others) were uniformly friendly and encouraging.  Everyone is so easy to talk to and so welcoming of others.  I’m more convinced than ever that Groovy and Grails are going to be huge in the marketplace.
  3. Glen Smith, on the other hand, is certifiably insane.  And I mean that in the nicest possible way. 🙂  My biggest disappointment at the conference (other than bizarrely forgetting to bring my copy of DGG to get autographed) was that Glenn’s “UI Extreme Makeover” talk was so full I was unable to find a seat.  I had to settle for yet another talk by Scott Davis instead (the horror, the horror).
  4. Apparently I’m not the only person having a long-term love affair with GinA.
  5. Oh, and buy Groovy Recipes!  It’s now available!
  6. Dierk Koenig gave a talk entitled “7 Groovy Usage Patterns for Java Projects.”  In retrospect, that may have been the most important talk I attended.  He showed all sorts of ways to apply Groovy to your projects, and gave each category a clever name.  My favorite was “house elf” scripts, defined as programs that “delegate the housework,” i.e., do the everyday background work for you.  I’m going to start collecting my own Groovy programs into his categories.  I really hope he finds a place to publish that presentation, or some article based on it.
  7. Jason Rudolph’s Refactotum presentation (basically a how-to on ways to participate in open source projects) got off to a slow start, but finished very strong.  I’ve never actually contributed to an open source project, but now that I know how, I’m sure I’ll be doing so in the future.  I can write test cases at least, even when I’m otherwise busy.  I’m equally sure I’ll mention something about that here. 🙂
  8. Don’t forget to buy Groovy Recipes!  Don’t let the fact that Scott included a quote from me in it dissuade you.

I see that I’ve left out almost everything.  I wish I could have attended twice as many talks.  I guess I’ll just have to go back next year, too.

Categories
Grails

Additional comment about GRAILS_HOME

Yesterday I commented on how I needed to change my GRAILS_HOME variable to point to the grails subdirectory of the grails-1.0 distribution. I did a bit more checking and discovered something interesting.

I’ve been using the Windows installer for Grails. I like how it also installs links for all the JavaDoc documentation, how it includes optional components, etc. It turns out, though, that it’s the Windows installer that’s changed the directory structure.

I just download the zipped binary distribution and expanded it, and it has the original directory structure. With that, I can just set the GRAILS_HOME variable to c:\grails-1.0.1 as usual and everything should be fine.

I’ll be watching to see if the Windows installer changes to go back to the original structure or not. If so, I’ll be sure to mention it here. I probably should say something on the mailing list, too, which I might if I get a chance.

Categories
Grails

Grails Home moved in 1.0

Like approximately 15,000 others, I downloaded and installed Grails 1.0 final in the couple of days after it was released. To my great surprise, however, I couldn’t get it to run properly any more, especially in either Eclipse or IntelliJ IDEA. I couldn’t get either IDE to understand Grails 1.0. I kept trying to set the Grails configuration in IDEA to c:\grails-1.0, and it kept saying that that was an invalid Grails directory. My Eclipse installation messed up the entire build path.

For about a week now, I’ve been assuming my IDE problems were related to plugins that hadn’t yet been updated. I kept checking to see if they were going to update any time soon.

That, as they say, turned out not to be the issue.

This evening I finally realized what happened. Between the last release candidate and the final release of 1.0, the directory structure of the installer changed. Now, the real location of GRAILS_HOME is the grails directory underneath the grails installer. In other words, my GRAILS_HOME variable is

GRAILS_HOME=c:\grails-1.0\grails

whereas before it would have been the grails-1.0 directory itself. Now that I’ve updated that, I’ve been able to get both IDEA and Eclipse to work.

What surprises me is that I can’t find this change documented anywhere. I don’t see it on the Grails wiki, or anywhere in the mailing list archives. Either this is obvious to everyone, or it just hasn’t been recorded anywhere yet.

Thus, this post. Hopefully it’ll help somebody else who might be having the same problem.

(Incidentally, to be strictly correct, I actually set my location to c:\grails-1.0.1\grails. Tonight I updated to the incremental version 1.0.1. :))

Categories
Grails Groovy

All I know I learned from GinA (and DGG)

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 Dates 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. 🙂

Categories
Grails Groovy

Looking forward to 2G X

Saturday my RSS reader brought a message saying that registration was now open for the Groovy/Grails Experience (called 2G Experience on the website, but I like the shorthand 2GX).  Since it’s in the DC area, and I live in CT, I hesitated for a couple of hours.  After all, I’m a one-person company, so even though it’s a legitimate business expense, it’s still coming out of my pocket.

Then I came to my senses.  Graeme Rocher himself is going to be there, and two of my good friends in the community are going to be making presentations, Scott Davis (author of the upcoming best-seller Groovy Recipes) and Jason Rudolph  (author of one of my all-time favorite technical books, Getting Started with Grails).  Even beyond that, the list of presentations looks fantastic.

This conference is SO going to rock.  Like most conferences in the No Fluff, Just Stuff family, attendance is limited, in this case to only 300.  I will be quite interested is seeing how quickly those tickets sell out.  If my assessment of the building momentum behind Groovy and Grails is anywhere near correct, they’ll be gone by the first week in January at the latest.

I remember all the excitement that surrounded the First International Grails conference a couple of years ago.  Groovy and Grails haven’t gotten the same press yet.  But both have been gaining mindshare steadily, and they still feel to me like they’re inevitable.  I’m really glad, too, because they’re so much fun.

I’d buy my ticket now if I were you. 🙂

Categories
Grails Groovy

Groovy Dates are Ranges too

I have a Grails application that I use to keep track of training courses that I teach. For each course, I enter the start and end dates, among other information. It’s a pretty straightforward application that I’ve described here in several previous posts.

I’ve gotten to the point now where I want to do more than just store the data. I added a Google Maps display to show the locations of my training classes, which was pretty fun. I even got to put geodesic polylines on the map to connect my home to the various cities where I taught, and clicking on the markers lets me see the name of each course, the city and state information, and the dates I was there.

Now, however, I want to figure out how many days I’ve been teaching, both on a month-to-month basis as well as yearly. I’ve only been on my own for a bit under three years, and I’m still trying to figure out the right balance between teaching days and development days, while keeping the travel at least somewhat reasonable. Of course, I have to meet my own revenue projections as well. 🙂

Computing the number of days taught is what lead me to another exploration of Groovy capabilities. Dates and times are always a mess in Java (one might easily call them a disaster). I was dearly hoping that managing them in Groovy would be substantially easier, because, after all, everything in Groovy is substantially easier.

Here’s the problem: For each year, I want to populate an array indexed by month, where each element of the array contains the total number of days taught that month. What I have available is the start and end dates (as instances of the Date class) for each month. How do I process all my courses to date to populate that days array for each year?

I’m sure there are many different ways to solve this problem (and I welcome alternative suggestions), but here’s how I’ve gone about it so far. In my Grails application, I created a class called CourseStatistics and put it in my src/groovy folder. After all, it’s neither a domain class nor a controller class. That class looks like this:


class CourseStatistics {
  List courseList
  def teachingDays = [:]

  def computeTeachingDays() { ... }
}

The idea is that I instantiate the class, call the setCourseList() method with my complete list of courses (Course.list() makes that simple), then invoke computeTeachingDays() to populate the hash. My current implementation assumes that the hash will use the years as keys, and the values will be 12-element arrays representing days taught in each month that year.

Here’s the big complication. Some classes start in one month and end in another. For example, earlier this year I taught a class on the Spring Framework that started on April 30 and ended on May 2. So I can’t just count the days (even assuming that was easy with Dates) and add them to a particular month. I need to be able to iterate through the dates for each course, identifying which month it belongs to, and increment the proper month value.

I tried all sorts of odd constructs — while loops and such. Usually with a collection I just invoke the each() method and use the individual elements that way, but I don’t have that here. All I have for each course is a start date and an end date.

Once again, Groovy came to the rescue. Groovy dates are Ranges, in the Groovy sense of the word. I presume this is because they implement the plus(int) operator, where the argument is the number of days, but I haven’t confirmed that yet. The result is that it is perfectly fine to write a loop like this:

for (date in startDate..endDate) { ... }

and the date variable will take on the value of each date in the range. How cool is that?

The rest is just the standard, awkward date/time manipulation stuff that the Date and Calendar classes require in Java, though I was able to simplify the Groovy code a bit:


def computeTeachingDays() {
  Calendar cal = Calendar.instance
  courseList.each { c ->
    for (date in c.startDate..c.endDate) {
      cal.time = date
      int month = cal.get(Calendar.MONTH)
      int year = cal.get(Calendar.YEAR)
      if (teachingDays[year]) {
        teachingDays[year][month] += 1
      } else {
        teachingDays[year] = new int[12]
        teachingDays[year][month] = 1
      }
    }
  }
}

In Groovy, I just call Calendar.instance to invoke the getInstance() method, of course. Then it turns out that the time property on the calendar can be set to an individual date, and since this is Groovy, I can just assign it rather than call the associated setter method. I’m also using the year as the key in the hash map. Then, since I know that the value is going to be a 12-element array of ints, I figured why not just dimension it with the proper (static) type each time I add a new year?

Once this method is finished, I have a complete hash map with integer arrays holding the number of teaching days for each year. I had to go through some odd machinations to display it in my view (and it’s still awkward, since so far I’ve been forced to display it with months as the column headers rather than years), but that’s a story for another post.

The good news is that this entire computation is much, much simpler than the corresponding code would be in Java, and it’s easier to read and understand to boot. Once again, Groovy rocks.

(Someday I’ll comment on the current debate going on between people trying to decide whether to go with Groovy or JRuby, but it’s pretty obvious which side I happen to favor. Again, that’s a post for later.)

Categories
Google Maps Grails Groovy

Moving My Google Maps Mashup to Grails, part two

I made a few improvements to my Google Maps mashup, and though they’re not as significant as the changes made in my previous posts, I still wanted to make a record of them.

I discussed in my last post the problem I had exposing my domain objects as JSON strings. To summarize the problem:

  1. In order to use the Google Maps API, I need to be able to pass latitude and longitude information from my Location objects (written in Groovy) into JavaScript. Grails pulls the data from a database and populates all the Locations, which are passed from the controller to the view. Somehow the JavaScript in the view needs to extract the coordinates in order to draw the markers on the map.
  2. The page where I want to do the rendering actually lists courses, not just locations. My Course class has an attribute of type Location, which is fine, but means I need to traverse a relationship in order to access the data I need. Normally that wouldn’t be an issue, but JavaScript doesn’t know how to do that.
  3. I can use the convenient Grails “converters” plugin to translate my Course objects into JSON, but that leads to difficulties as well. It seems that the converter doesn’t follow relationships, or, rather that it just inserts the foreign key value where the relationship goes. In other words, the JSON version of a particular Course only lists an integer in the location field.
  4. As a result of (3), I decided to pass both the collection of Course objects and the collection of Location objects to the view. That in itself was awkward, because it meant I had to “dereference” the location array myself for each course, which felt highly brittle.
  5. Also, when the converter operated, it dumped all the information about my objects directly into the web page, where it could be read by anyone who knows how to do a “view source” command. Not good.

The way I solved the problem was to create a special class in my Grails project called CourseMapData:


class CourseMapData {
    String title
    Date startDate
    Date endDate
    String city
    String state
    double latitude
    double longitude
}

That class encapsulates all the data I need out of both the Course object and its associated location. It also leaves out any attributes that might possibly be sensitive. Since it doesn’t have any associations to any other classes, I can transform it into JSON without losing any information.

Since it’s not actually a domain class, I put the source file in the src/groovy folder of my Grails project. In my CourseController, I then added the necessary translation:


def list = {
    if(!params.max)params.max = 10
    def courseList = Course.list(params)
    def cmList = []
    courseList.each { c ->
        def cm = new CourseMapData()
        cm.title = c.title
        cm.startDate = c.startDate
        cm.endDate = c.endDate
        cm.city = c.location.city
        cm.state = c.location.state
        cm.latitude = c.location.latitude
        cm.longitude = c.location.longitude
        cmList << cm
    }

    [ courseList:courseList, cmList:cmList]
}

I was hoping their might be a more elegant way to populate the object (something similar to how request parameters are passed into objects using “obj.properties = params“), but it was easy enough to just write them out. That’s especially true because the values come from both Course and Location.

By passing the collection of CourseDataObjects, my JavaScript code in my list.gsp page reduces to:


function initialize() {
    map = new google.maps.Map2($("map"));
    var homeJSON = ${Location.findByCityAndState('Marlborough','CT').encodeAsJSON()};
    home = new google.maps.LatLng(homeJSON.latitude,homeJSON.longitude);
    map.setCenter(home, 4);

    var cmsJSON = ${[cms:cmList].encodeAsJSON()};
    var courseDataArray = cmsJSON.cms;

    processCourses(courseDataArray);

    map.enableScrollWheelZoom();
    map.addControl(new google.maps.LargeMapControl());
    map.addControl(new google.maps.MapTypeControl());
    map.addControl(new google.maps.OverviewMapControl());
}
google.setOnLoadCallback(initialize);

Now I’m only converting one collection (cmList), and although it dumps a lot of code in the page source, nothing in it is particularly sensitive. The rest is essentially the same as before.

I’m still not quite ready to put my application online, because it hasn’t been secured yet. I plan to use a beforeInterceptor to separate the admin pages from the public pages. It’s a little awkward, though, because this isn’t a site that I actually want anyone but myself to modify. Still, I’d like to be able to access it myself when I’m traveling in order to add in new courses and/or locations.

Hopefully I’ll get a chance to add that functionality soon.

As an aside, I fixed another issue that occurred to me. Obviously I don’t want to have to enter a latitude and a longitude when I create a new Location. I have an earlier post here that talks about using the Google geocoder service to do the translation for me. I finally got around to adding that functionality to my Grails application.

Now the save method in my LocationController looks like:


def save = {
    def location = new Location()
    location.properties = params
    if (location.latitude == 0 && location.longitude == 0) {
        // Use the Google Maps geocoder to fill in latitude and longitude
        def key = 'longUglyGoogleMapsKey'
        def base = 'http://maps.google.com/maps/geo'
        def query = "q=${location.city},+${location.state}&output=csv&key=${key}"
        def url_string = base + '?q=' +
            ',+' + URLEncoder.encode(location.city,"UTF-8") +
            ',+' + URLEncoder.encode(location.state,"UTF-8") + 
           "&output=csv&key=${key}"
        def results = new URL(url_string).text.split(',')
        location.latitude = results[-2].toDouble();
        location.longitude = results[-1].toDouble();
    }
    if(location.save()) {
        flash.message = "Location ${location.city}, ${location.state} created."
        redirect(action:show,id:location.id)
    } else {
        render(view:'create',model:[location:location])
    }
}

To use it, I modified my create.gsp page for Locations to include the phrase:

“Note: leave latitude and longitude empty or 0.0 to use the embedded geocoder.”

Then when I enter the data, I can see the latitude and longitude fill in automatically. Sweet.