Monday, 23 January 2012

Grails plugin: use the "loadAfter" property

I recently wrote a plugin that will extend the class of one of Grails' beans, and replaces Grails' bean with itself. However, I ran into a funky error where sometimes my bean had replaced Grails', and at other times the Grails default bean was in the context.

Turns out that we can use the "loadAfter" property in the PluginNameGrailsPlugin.groovy class.

By specifying a list of plugin names here, we can control the order that grails will load your applications. In my instance, my plugin was sometimes being loaded before the controllers and groovy pages plugins. I managed to consistently get my plugin to load after these two using the following:


def loadAfter = ['controllers', 'groovyPages']


See the official documentation for more information.

Thursday, 12 January 2012

Migrating a Grails app from 1.1 to 2.0

We're undertaking the task of upgrading a rather large Grails 1.1 plugin to Grails 2.0, and the apps that depend on it. We shall document any oddities and issues that we encounter along the way.


+----------------------+-------+-------+
| Name | Files | LOC |
+----------------------+-------+-------+
| Controllers | 58 | 3903 |
| Domain Classes | 26 | 606 |
| Services | 27 | 2626 |
| Tag Libraries | 11 | 2755 |
| Groovy Helpers | 48 | 2030 |
| Java Helpers | 1 | 278 |
| Unit Tests | 48 | 5632 |
| Integration Tests | 2 | 61 |
+----------------------+-------+-------+
| Totals | 221 | 17891 |
+----------------------+-------+-------+


should be fun!

Wednesday, 11 January 2012

Extending Grails' beans with your own subclass

We've just completed a small Grails 2.0 plugin. One of the things this plugin does is to replace Grails' "groovyPageLocator" bean with our subclass implementation that changes the way the class behaves slightly. It was a little tricky to get my subclass injected with its dependencies also injected correctly, so I thought I'd write up a quick blog to document how we did it.

When it came to defining our bean, I basically wanted to override the groovyPageLocator, defined in "doWithSpring" in the GroovyPagesGrailsPlugin. I managed to do so by basically copying/pasting the bean definition and to change the bean class - but this seemed a nasty hack. If, in future Grails versions this plugin changes, we'd have to mirror the change in our plugin...plus, y'know, code duplication and all.


As the doWithSpring closure is just setting up the template for Spring to wire-up our beans, we can fiddle around with the beans here to achieve what we want.

In the end, here's the code we used to inject our own subclass in place of Grails' bean:


def doWithSpring = {
def pageLocator = delegate.getBeanDefinition("groovyPageLocator")
pageLocator.beanClass = PluginAwareGroovyPageLocator
pageLocator.propertyValues.add("order", "someValue")
}


So, we're just grabbing the existing bean from the definition, changing its class to point to my new class and adding in a custom property that's specific to my subclass. Lovely!

Wednesday, 4 January 2012

Taglib dependencies in Grails 2 unit tests

I'll essentially be reposting information that I found at Ad-Hockery (an excellent blog, by the way), but it was an annoying issue that took me some time to get working, so it's worth reposting.

Given a Grails controller as such:


class SomeController {
def something

def index() {
render something.method()
}
}


And its test:


import grails.test.mixin.*

@TestFor(SomeController)
class SomeControllerTests {

void testIndex() {
// given
controller.something = [method: { "hello!" }]

// when
controller.index()

// then
assert response.text == "hello!"
}
}


All works fine. So, given a very similar situation for a taglib:


class SomeTagLib {
static namespace = "blargh"
def something

def index = { attrs, body ->
out << something.method()
}
}

// ------

import grails.test.mixin.*

@TestFor(SomeTagLib)
class SomeTagLibTests {

void testIndex() {
// given
tagLib.something = [method: { "hello!" }]

// when
def output = applyTemplate("<blargh:index />")

// then
assert output == "hello!"
}
}


This would not work at - it was throwing a NullPointerException in the taglib itself, indicating my 'service' had not actually been wired in correctly.

Thanks to the blog post over at Ad-Hockery, I got this to work in the end by modifying my test as such:


import grails.test.mixin.*
import org.junit.Before;

@TestFor(SomeTagLib)
class SomeTagLibTests {

SomeTagLib tagLib

@Before
void setUp() {
tagLib = applicationContext.getBean(SomeTagLib)
}

void testIndex() {
// given
tagLib.something = [method: { "hello!" }]

// when
def output = applyTemplate("<<blargh:index />")

// then
assert output == "hello!"
}
}


I'm unsure about submitting a JIRA report for this as it may be expected behaviour; personally I'd expect the taglib tests to work as the controller ones do. hmm.

Tuesday, 3 January 2012

Tunnelling to CloudFoundry services with Ubuntu


Have been waiting for a way to access my mongodb instances on CloudFoundry without having to access it via code, so was pleased to see this blog post on CloudFoundry (http://blog.cloudfoundry.com/post/12928974099/now-you-can-tunnel-into-any-cloud-foundry-data-service)

However, after following the instructions I had a few issues getting it running on Ubuntu so here are some notes of what I did.

Trying to install the caldecott gem with
sudo gem install caldecott
I got the following error:
ERROR: Error installing caldecott:ERROR: Failed to build gem native extension.
/usr/bin/ruby1.9.1 extconf.rb...
makeg++ [...]make: g++: Command not found
I took a look and on my system I do have a copy of g++ (binary called g++-4.4) but wasn't aliased to g++. A quick google got me here http://stackoverflow.com/questions/4735303/failed-to-install-gem-install-eventmachine-i-need-starling-in-my-project-fo.

The issue was easily solved by running
sudo apt-get install build-essential
which configured all the relevant bits and pieces.

Then back to the install of caldecott itself which then failed again with a complaint that rack wasn't installed (dependency of sinatra) so a quick "sudo gem install rack" sorted that.

So once that was sorted it was time to try it... except it didn't work. Ran the following:
vmc tunnel [database name]
and got the following error back:
To use `vmc tunnel', you must first install Caldecott:
gem install caldecott
Note that you'll need a C compiler. If you're on OS X, Xcode
will provide one. If you're on Windows, try DevKit.
This manual step will be removed in the future.
Error: Caldecott is not installed.
Back to Google and found this question posted to the CloudFoundry newsgroup (http://support.cloudfoundry.com/entries/20693163-trouble-installing-caldecott) which suggested installing bundler with:
sudo gem install bundler
And finally it all works as advertised.