I am looking for ways on how to cleanup my Grails controller code. In various controllers i more or less have the same logic..
get the object
check if it exists
etc..
Is there a suggested way on making controller actions reuse common code?
--- solution ---
All answers to the question have contributed to the solution we have implemented.
We created a class that is used in our controllers using the Mixin approach. One of the methods that the mixin exposes is the withObject method. This method takes the domainname from the controller and uses this a base for the method. This behaviour can be overridden of course!
def withObject(object=this.getClass().getName()-"Controller", id="id", Closure c) {
assert object
def obj = grailsApplication.classLoader.loadClass(object).get(params[id])
if(obj) {
c.call obj
} else {
flash.message = "The object was not found"
redirect action: "list"
}
}
So all answers have contributed to the solution! Thanks a lot!
I always pull out this blog post when this question comes up:
http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/
Basically you have a private helper for various domains in your controllers.
private def withPerson(id="id", Closure c) {
def person = Person.get(params[id])
if(person) {
c.call person
} else {
flash.message = "The person was not found."
redirect action:"list"
}
}
The way you code the getter is very flexible and a typical use for me (that is not covered in the blog) is for editing etc.
I normally code this way (i like the pattern for its clear division and readability):
def editIssue() {
withIssue { Issue issue ->
def issueTypes = IssueTypeEnum.values().collect {it.text }
[issueTypes:issueTypes,activePage:"issue", issue: issue]
}
}
def doEditIssue(IssueCommand cmd) {
if(cmd.validate()) {
withIssue { Issue issue ->
issue.updateIssue(cmd)
redirect(action: "show", id: issue.id)
}
}
else {
def issueTypes = IssueTypeEnum.values().collect {it.text }
render(view: "edit", model:[issueTypes:issueTypes,issue:cmd,activePage:"issue"])
}
}
With my getter helper being:
private def withIssue( Closure c) {
def issue = Issue.get(params.id)
if(issue) {
c.call issue
}
else {
response.sendError(404)
}
}
I do think that the mixin method (very similar to the 'extend a common abstract controller' way) is nice too, but this way gives two advantages:
You can type the helper, like you see I do in the closure giving you access to the methods etc in STS/IDEA (not tested Netbeans)
The repetition is not very high, and the ability to change the getter (to use for example BarDomain.findByFoo(params.id) etc)
In the view I bind to edit() I just put an id="${issue.id}" in the <g:form> and it works seamlessly.
I wouldn't recommend inheritance for that, as you can't spread generic methods in several super classes. Your abstract class would quickly become messy if you have many controllers. You can't use composition (for instance using a Service) because you don't have access to response, render, or params directly from there.
The approach I use is to inject generic methods via Mixins.
#Mixin(ControllerGenericActions)
#Mixin(ControllerUtil)
class BookController {
def show = &genericShow.curry(Book)
def exists = {
render(idExists(Book))
}
}
The first action show uses a generic method in ControllerGenericActions.groovy, with an argument binded to it. The second use of a mixin idExists method is inside a controller action.
Here is an example code for src/groovy/ControllerGenericActions.groovy
class ControllerGeneric {
def genericShow(Class clazz) {
render clazz.get(params.id) as XML
}
}
and in src/groovy/ControllerUtil.groovy
class ControllerUtil {
def idExists (Class clazz) {
return clazz.get(params.id) != null
}
Not very useful in this case, but you get the idea.
Implement abstract controller with common methods (use 'protected' directive) and extend from it your real controllers. Do not use 'get' and 'set' words at the beginning of this method's names. Not good, but it works.
Related
If I want to share an object between multiple methods in a controller, is it wrong to create an instance var? E.g.
class MyController {
def index() {
def user = verifyUserLogin()
[messages:getMessages(user)]
}
private verifyUserLogin() {
...
return user
}
private getMessages(user) {
...do something with `user`...
return messages
}
}
would become
class MyController {
private user
def index() {
verifyUserLogin()
[messages:getMessages()]
}
private void verifyUserLogin() {
...
this.user = user
return
}
private getMessages() {
...do something with `user`...
return messages
}
}
Grails controllers are not singleton they are created for every request so it should not be any issue to have instance level variables.
However I personally prefer to not to declare instance level variables as much as possible, because when code starts becoming lengthy it becomes difficult to figure out the flow of the code that initializes and uses the variables.
I rather choose to pass them as parameters. However there's no such rule of thumb and it highly depends on the problem at hand.
The traditional way - for Servlets at least - of storing state between controllers, i.e. passing information between requests is to use the session object.
class MyController(){
def index(){
session.user = getUser()
}
def postex(){
def user = session.user
}
}
In grails 3.2.1 the default scope of controllers changed from "prototype", controller instance per request, to "singleton", one instance of each controller.
This made instance variables in controllers no longer safe unless you change the scope!
https://docs.grails.org/3.2.1/ref/Controllers/scope.html singleton
https://docs.grails.org/3.2.0/ref/Controllers/scope.html prototype
If you do want to use instance variables in a controller change the scope static scope = "prototype".
Rather than use instance variables you can set variables on the request (as request attributes). request.user = user Note that these will persist for the life of the request (visible to all interceptors and views traversed by the request).
I've to display check box checked or not based on some logic which is more than 4 lines of groovy code ...
I don't like to write it in GSP page..is there any taglib or some way I can extract the logic out of GSP page.
where I could access model boject and request objects.
You have (at least) 3 options:
Model attribute
If you're only doing the complex logic for a single page or a single controller, do the logic in the controller method and pass the boolean result to the view through a boolean:
// in your controller
def myAction() {
[shouldDrawCheckbox: shouldDrawCheckBox(...)]
}
private boolean shouldDrawCheckBox(/* info for decision making */) {
// decision making
}
Service method
If you're going to access this identical logic from several controllers, you can extract the shouldDrawCheckBox method into a service and again pass the result through the models.
class MyController {
def myService
def myAction() {
[shouldDrawCheckbox: myService.shouldDrawCheckbox(...)]
}
}
class MyService {
boolean shouldDrawCheckBox(...) {
// logic!
}
}
Custom Taglib
If you want to avoid passing the decision through the model, or if the logic is more generally applicable, you can create a custom taglib.
class MyTaglib {
static namespace = "my"
def myCheckbox = { attrs ->
// extract decision info from the attrs
// perform logic with info
if (shouldDrawCheckbox)
out << g.checkbox(attrs: attrs)
}
}
}
In your view:
<my:myCheckbox whateverYourAttribsAre="value" name="..." value="..."/>
A TagLib is a good palce to put your logic, you can pass what you need as attributes and do your test:
class MyTagLib {
static namespace = "my"
def somecheckbox= { attrs ->
def model = attrs.remove('model')
if() { //tests goes here
//you can also test if you need to mark the checkbox as checked
if() {
attrs.checked = "checked"
}
out << g.checkbox(attrs: attrs) //remaining attrs will be applied to the checkbox
}
}
}
myview.gsp
<my:somecheckbox model="${model}" name="checkboxname" value="${checkboxValue}" />
Ideally the logic should go to the controller which renders the gsp and sets a flag in the model object. If the gsp is a child of a template, then the flag has to pass through. DOM manipulations in view layer is not ideal when we have appropriate binding framework available in grails.
Use the g: namespace. http://grails.org/doc/2.2.x/ref/Tags/checkBox.html
<g:checkBox name="myCheckbox" value="${condition}" />
It doesn't get any simpler then this. All the logic should be done inside the controller.
All the data you need on a page can be passed by the controller. Just return a Map.
class MyController {
def index() {
def someCondition = true
[request:request, condition:someCondition]
}
}
I need to make changes to other domain classes when an instance of a particular domain class is deleted. What is the best way to do this? I don't want to wait until commit or flush so I don't think the "beforeDelete" callback will help. I would like to "override" delete, do some stuff and call super.delete():
class Foo {
Bar bar
void delete() {
if (bar) bar.foo = null
super.delete() -- this doesn't work
}
}
Currently I have named "delete" cancel but would like to call it "delete" but then I cannot call the original delete().
To add to what #sbglasius said, here's the link to the docs on GORM events
Complete example:
class Foo {
Bar bar
def beforeDelete() {
if(bar) {
bar.foo = null
}
}
}
I haven't tried overriding GORM methods myself, but this might give some insight on what's involved:
"Overloading" standard GORM CRUD methods
I would put the "delete" logic in a service and call that instead:
class FooService {
def deleteInstance(foo) {
if (foo?.bar) {
foo.bar.foo = null
// might have to call foo.bar.save() here
// then foo.bar = null
}
foo.delete()
}
}
Wanna do the following:
BootStrap {
def init = {servletContext ->
........
MyDomainClass.metaClass.save = {->
delegate.extraSave()
//////// how to call original save() here?
}
}
.........
}
P.S. MyDomainClass#extraSave is defined as public void extraSave(){.....}
First of all, Bootstrap.groovy may not be the best place to do this kind of metaprogramming. The problem with this approach is that the changes to the classes will be applied when the application starts, but you may lose these changes when the application is reloaded. Obviously this is only an issue during development, and not an issue at all if you don't mind restarting the server every time you make a change, but I'll bet this would quickly become a major annoyance. In order to have the changes applied when app is reloaded as well, you should move the metaprogramming into a plugin, where you can hook into the onChange application lifecycle event.
So the steps are:
Create a plugin
Do the metaprogramming in the doWithDynamicMethods and onChange closures of the plugin descriptor
Here's a complete example where I "override" the chain() method on all the controller classes. The code to do likewise for the save() method of domain classes should only require some obvious replacements, e.g. use application.domainClasses instead of application.controllerClasses
def doWithDynamicMethods = {ctx ->
application.controllerClasses.each {controller ->
replaceChain(controller)
}
}
def onChange = {event ->
if (application.isArtefactOfType(ControllerArtefactHandler.TYPE, event.source)) {
def clz = application.getControllerClass(event.source?.name)
replaceChain(clz)
}
}
private replaceChain(controllerClass) {
// Save a reference to the grails chain() method
def grailsChain = controllerClass.metaClass.pickMethod("chain", [Map] as Class[])
controllerClass.metaClass.chain = {Map params ->
println "My code to execute before chain goes here"
// Invoke the grails chain() method
grailsChain.invoke(delegate, [params] as Object[])
println "My code to execute after chain goes here"
}
}
why not leveraging the GORM events for this purpose? In the Domain class:
def extraSave() {
// ...
}
def beforeInsert = {
extraSave()
}
def beforeUpdate = {
extraSave()
}
IMHO this a cleaner approach. Documentation can be found here
Not sure if the following works, but this might be a solution:
MyDomainClass.metaClass.origSave = MyDomainClass.metaClass.save
MyDomainClass.metaClass.save = {->
delegate.extraSave()
delegate.origSave()
}
Please give me feedbeck if the above worked...
In a Grails application I would like to add a foo() method to all my controller classes. I know that I can do this inside a plugin's doWithDynamicMethods closure using code like:
application.controllerClasses.toList()*.metaClass*.foo = { println 'foo called' }
However, I don't want to create a plugin just for this purpose. Is there anywhere else I can do this. I suspect it might be possible within the init closure of BootStrap.groovy, but I don't know how to get access to the GrailsApplication instance in this closure.
Thanks,
Don
def grailsApplication = org.codehaus.groovy.grails.commons.ApplicationHolder.application
The question was asked a long time ago, so my answer might not have been possible back then - but now it's April 2014. This code shows the most straightforward way of adding a method to all your controllers (from within BootStrap.init):
grailsApplication.controllerClasses.each { controllerClass ->
if (controllerClass.clazz.name.contains("org.idurkan.foo")) {
controllerClass.metaClass.respondError = { String message, int status = 404 ->
response.status = status
render([errorMessage: message] as JSON)
}
}
}
In BootStrap.groovy, inject grailsApplication like this: def grailsApplication
Make a call like mine above, substituting your own package name - to avoid messing around with plugins' classes.
Now in any controller you can use the repondError closure (invoke it like a method).
Note this does not add an action! It's just a utility closure/method available in every
controller.
class BootStrap {
def grailsApplication
def init = { servletContext ->
...
}
}