I need to add common actions to a number of my controllers without using inheritance. All our controllers extend an Abstract controller and the functionality I want to include does not make sense in the abstract controller.
My initial idea was to use a Mixin, but it appears that actions, since they are closures, are not 'mixed-in' to the controllers that include the mixin, only methods are.
Can anyone suggest a way I can get "mixin' like functionality to add actions to controllers?
thanks!
The way to do it is to iterate over your controllers and add methods to it using metaprogramming.
For one example, check out perf4j plugin (file Perf4jGrailsPlugin.groovy).
In it, you'll see something like:
application.controllerClasses.each() {
addPerf4jMethods(it.clazz, log)
}
def addPerf4jMethods(artefactClass, log) {
log.info "Adding Per4j methods: ${artefactClass}..."
artefactClass.metaClass.withStopwatch << { Closure callable ->
withStopwatch("", null, callable)
}
artefactClass.metaClass.withStopwatch << { String tag, Closure callable ->
withStopwatch(tag, null, callable)
}
}
In the code above, you are iterating through all controllers and adding methods withStopWatch, so they are now available everywhere. If you only have to do it in some controllers, you can obviously put in additional checks.
You can put this code into BootStrap.groovy, so it runs every time app starts up.
All our controllers extend an Abstract
controller and the functionality I
want to include does not make sense in
the abstract controller.
Then why not have a second abstract controller that extends the base controller and is in turn extended by only those controllers that need this functionality?
This sounds like the easiest and cleanest solution to me - certainly better than using metaprogramming. "Prefer composition to inheritance" does not mean that inheritance is a fundamentally dirty thing that should be avoided at all costs.
A third option that I often use in this type of situation is a static import. This is actually a part of java (not groovy) that many people aren't aware of.
So if you have a class like this with the static methods that you want to share:
class Foo {
static baz = { ->
return "baz!"
}
}
And use "import static" in the classes that you want to have that method available:
import static Foo.baz
class Bar {
static void main(String[] args) {
assert "baz!" == baz.call()
}
}
No superclass or mixin magic needed, let the compiler do the work for you.
According to the new grails dynamic-controller plugin, it:
allows you to dynamically create controllers and add actions to existing controllers
using mixins.
It seems to match your requirements.