Last updated by 5 years ago

Page: Script hooks, Version:0

Script hooks

We need a mechanism to allow plugins to hook into default Grails scripting mechanisms. This will then allow plugins to get closure to truly encapsulating the functionality that core Grails code can achieve.

For example, the current createArtifact script target has hardcoded special knowledge, such as creating a new controller requires creating a views/<controllerName> dir.

This should not be the case, and requires maintainence in a core Grails file (Init.groovy) if future plugins are to have similar requirements.

Secondly, I believe we could make Grails script output more friendly, especially in a training or presentation environment, or even in IDEs. A lot of Ant related stuff comes to System.out as well as printlns scattered here and there.

We should be able to run Grails scripts in a default mode that includes not output except some very specific high level output i.e.:

grails create-domain-class Book

Welcome to Grails v0.4.2

Caching scripts… Created domain class Created unit test Domain class Book created.

There is far too much noise currently.


I want us to be able to change Init.groovy so that createArtifact triggers callbacks into any and all interested plugins, so that those plugins can take actions related to the artifact, such as creating a directory, creating unit tests etc.

I want to be able to use notification tools like Growl on Mac OS X to give me status updates on Grails scripts.

Current status

I have successfully modified my local scripts to trigger Growl notifications when a domain class has been created. The code for this was put in a user-specific file in ~/.grails/scripts and Init.groovy was changed to conditionally load this code. It provides us with pluggable "status update" mechanism which can be overidden by users-specific scripts - i.e. the default implementation can do nothing, or println the info, but when you add your own script that code will be used.

Proposal Details (Option #1)

This solution will support calling of event handlers for common script activities, which may be hooked into by plugin scripts, or a user-specific script.

In Init.groovy we add a piece of code to check for ~/.grails/scripts/Hooks.groovy and scan all plugins for a scripts/Hooks.groovy file, and load each as a Groovy Script (not Gant script) - we keep these in a list. Then we add closures for all common actions, and these iterate over all the scripts, getting the closure of the same name from each script and calling it.

So for example in Init.groovy we would probably define at the very least:

afterCreateArtifact = { typeName, className ->
    allHooks.each() { it.afterCreateArtifact(typeName, className) }

statusUpdate = { message -> allHooks.each() { it.statusUpdate(message) } }

// status update that indicates it is the last thing a script is doing statusFinal = { message -> allHooks.each() { it.statusFinal(message) } }

Using this it is then trivial to add plugins that have Hooks.groovy files that do things like:

// Auto add artefacts to SVN
afterCreateArtifact = { typeName, className ->
    svnAdd( "$basedir/grails-app/$typeName/$className.groovy" )

statusUpdate = { message -> growlNotify(message) }

statusFinal = { message -> growlNotify("Completed: $message") beep() }

These hooks can be added to the user's own Hooks.groovy also to provide cross-project hooks. I have this concept working in a simpler form locally.

Option #2

I'm speculating here but we might be able to do some metaclass magic to get before/after advice on all targets being invoked, as well as calls to any function or closure. This would allow "hooks" to use some convention syntax to alter the behaviour of these.

The advantage of such a dynamic approach is that you don't have to define and maintain all the permitted hooks in Init.groovy.