views:

254

answers:

1

Hi,

I'm developing a plugin that adds a getFlashHelper method to each controller. This method should return an instance of a FlashHelper class.

However, the constructor of the FlashHelper class must be passed the instance of the controller on which the getFlashHelper method was called.

Hopefully the following code will explain what I'm doing a bit better

def doWithDynamicMethods = {ctx ->

    application.controllerClasses*.metaClass*.getFlashHelper = {

        def controllerInstance = delegate

        // Avoid creating a new FlashHelper each time the 'flashHelper' property is accessed
        if (!controllerInstance.metaClass.hasProperty('flashHelperInstance')) {
            controllerInstance.metaClass.flashHelperInstance = new FlashHelper(controller: controllerInstance)
        }

        // Return the FlashHelper instance. There may be a simpler way, but I tried
        // controllerInstance.metaClass.getMetaProperty('flashHelperInstance')
        // and it didn't work
        return controllerInstance.metaClass.getMetaProperty('flashHelperInstance').getter.invoke(controllerInstance, [] as Object[])
    }
}

The code appears to work, but I can't help feeling that there must be an easier way of doing this. That last line is particularly gruesome. Is there any way I can simplify this?

Thanks, Don

+1  A: 

Since controllers are created per-request, I'd store the helper as a Request attribute:

for (c in grailsApplication.controllerClasses) {
   c.clazz.metaClass.getFlashHelper = { ->
      def controllerInstance = delegate
      def request = controllerInstance.request
      def helper = request['__flash_helper__']
      if (!helper) {
         helper = new FlashHelper(controller: controllerInstance)
         request['__flash_helper__'] = helper
      }
      helper
   }
}
Burt Beckwith
If I use this approach I'll end up creating a lot more FlashHelper objects, i.e. one per request, instead of one per controller class. Notice that in the approach I've used, I create on FlashHelper per controller class/metaClass, not one per controller instance.Is there some advantage to this approach that I'm missing, apart from the slightly more legible code?Thanks!
Don
In standard Spring MVC controllers are singletons, but in Grails a new controller instance is created for each request. So we're both creating the same number of helper instances. Add a println or a log statement to your getFlashHelper closure to verify.
Burt Beckwith
But my approach will create one FlashHelper per Controller **class** (not one per controller instance), whereas yours will create one per request. I still don't see what the advantages of storing the FlashHelper as a request attribute, rather than a metaClass property are?
Don
If you do create one per class then you've introduced a large bug since the helper needs the controller instance. After the first request your helper will have a reference to the previous controller instance which is no longer available (and now probably not available for garbage collection). But you're not, you're creating one per instance.
Burt Beckwith