Grails More Impressions - Scripts

Posted December 7, 2009

For my next reflection on impressions with Grails, I'm going to look at scripts (in the context of little utility scripts to simplify common complex tasks).

For this entry, I will compare Grails' "gant" scripts with Ruby "rake" scripts.

First up, namespacing is very different between the two. Gant uses the name of the file itself to determine the namespace, converting camel-case into a dashed name (for example, "bootstrapDatabase" becomes "bootstrap-database"). Within each namespace, you can define several "targets". This whole idea of targets is very "Java ant" centric, which is to be expected given that gant is simply a Groovy-to-ant layer.

By comparison, rake doesn't use the name of the file in any significant way, but you must define a "namespace" within the rake script itself. These namespaces are embeddable, meaning you can have "this", "is", "a" as 3 seperate layers of namespaces (separated by ":"), and the actual task to run is aptly named "task".

The rake method actually makes a lot more sense, since it allows vastly more structure, and allows grouping of similar tasks into common namespaces. Gant really cannot do this with any degree of complexity.

Next, I'm going to discuss integration with the actual framework.

Unfortunately, this is the biggest area where gant is ... well ... crap. Accessing the underlying Grails framework from a gant script is an exercise is absolute frustration. Gant scripts, by default, do not initialize the Grails environment - you must do this manually:

import org.springframework.orm.hibernate3.HibernateTemplate

includeTargets << grailsScript('Bootstrap')

target(main: "Bootstraps the database - assumes an empty database!") {
    depends(configureProxy, packageApp, classpath)

    bootstrap()
    def template = new HibernateTemplate(appCtx.getBean("sessionFactory"))

    def countryClass = grailsApp.getDomainClass('Country').getClazz()
    if (!countryClass.findById('CA')) {
        def canada = countryClass.newInstance()
        canada.id = 'CA'
        canada.englishName = 'Canada'
        canada.localName = 'Canada'
        template.save(canada)
    }

    populator.process('Country')
}

setDefaultTarget(main)

This is very unfriendly, and was completely unexpected, considering the dynamic nature of the Groovy language. An important thing to note in the above is that you cannot access the domain classes directly - you have to perform a rather convoluted call to a "grailsApp" variable, which is automatically created in the "bootstrap()" call.

Now, the area that really got my blood boiling - after a week, I could not figure out how to get gant to use Grails plugins, nor even any class created in the Groovy source directory. At the time of writing, I still have no clue as to how to do either of these, and the Gant and Grails documentation on the matter is severly lacking. I'd happily contribute such documentation, if I could fathom how to do it.

By contrast, rake runs in a fully configured and ready to go Rails environment. All libraries and plugins and scripts are immediately available. Models, fixtures, anything can be executed directly within the rake tasks. This is a huge improvement over gant.

In this one area, I'm afraid Ruby on Rails has Grails beat, hands down. Perhaps once I'm more familiar with the Grails framework, I'll work on a better scripting engine other than gant.

Comments

I agree with you. This is overly complex. I wanted to create a simple script to update records in the db, but it appears that the best way for me to do it is by creating a hidden controller action to perform it. Very frustrating.

Displaying 1 comment

Add comment

Visit my Friends and Family

If you've enjoyed my site, please take a moment to visit my friends and family, many of whom have some interesting insights, and entertaining thoughts and ideas.