{"id":11684,"date":"2013-06-05T08:24:00","date_gmt":"2013-06-05T07:24:00","guid":{"rendered":"https:\/\/touk.pl\/blog\/?guid=8dccc3f1ce7cd54a65c62c973baaadde"},"modified":"2022-08-02T14:56:00","modified_gmt":"2022-08-02T12:56:00","slug":"simple-trick-to-dry-your-grails-controller-2","status":"publish","type":"post","link":"https:\/\/touk.pl\/blog\/2013\/06\/05\/simple-trick-to-dry-your-grails-controller-2\/","title":{"rendered":"Simple trick to DRY your Grails controller"},"content":{"rendered":"<p>Grails controllers are not very DRY. It&#8217;s easy to find duplicated code fragments in default generated controller. Take a look at code sample below. It is duplicated <strong>four<\/strong> times in <code><span class=\"method\">show<\/span><\/code>, <code><span class=\"method\">edit<\/span><\/code>, <code><span class=\"method\">update<\/span><\/code> and <code><span class=\"method\">delete<\/span><\/code> actions:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"groovy\">class BookController {\r\n    def show() {\r\n       def bookInstance = Book.get(params.id)\r\n       if (!bookInstance) {\r\n            flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])\r\n            redirect(action: \"list\")\r\n            return\r\n        }\r\n        [bookInstance: bookInstance]\r\n    }\r\n}<\/pre>\n<h3 id=\"why-is-it-duplicated\">Why is it duplicated?<\/h3>\n<p>There is a reason for that duplication, though. If you move this snippet to a method, it can redirect to <code><span class=\"string\">\"list\"<\/span><\/code> action, but it can&#8217;t prevent controller from further execution. After you call <code><span class=\"method\">redirect<\/span><\/code>, response status changes to 302, but after method exits, controller still runs subsequent code.<\/p>\n<h3 id=\"solution\">Solution<\/h3>\n<p>At <a href=\"http:\/\/touk.pl\/\">TouK<\/a> we&#8217;ve implemented a simple trick to resolve that situation:<\/p>\n<ol>\n<li>wrap everything with a simple <code><span class=\"method\">withStoppingOnRender<\/span><\/code> method,<\/li>\n<li>whenever you want to render or redirect <strong>AND<\/strong> stop controller execution &#8211; throw <code><span class=\"class\">EndRenderingException<\/span><\/code>.<\/li>\n<\/ol>\n<p>We call it <em>Big Return<\/em> &#8211; return from a method and return from a controller at once. Here is how it works:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"groovy\">class BookController {\r\n    def show(Long id) {\r\n        withStoppingOnRender {\r\n            Book bookInstance = Book.get(id)\r\n            validateInstanceExists(bookInstance)[bookInstance: bookInstance]\r\n        }\r\n    }\r\n    private void validateInstanceExists(Book instance) {\r\n        if (!instance) {\r\n            flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label',\r\n                default: 'Book'), params.id])\r\n            redirect(action: \"list\")\r\n            throw new EndRenderingException()\r\n        }\r\n    }\r\n}\r\nclass EndRenderingException extends RuntimeException {}<\/pre>\n<h3 id=\"example-usage\">Example usage<\/h3>\n<p>For simple CRUD controllers, you can use this solution and create some <code><span class=\"class\">BaseController<\/span><\/code> class for your controllers. We use <code><span class=\"method\">withStoppingOnRender<\/span><\/code> in every controller so code doesn&#8217;t look like a spaghetti, we follow DRY principle and code is self-documented. Win-win-win! Here is a more complex example:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"groovy\">class DealerController {\r\n    @Transactional\r\n    def update() {\r\n        withStoppingOnRender {\r\n            Dealer dealerInstance = Dealer.get(params.id)\r\n            validateInstanceExists(dealerInstance)\r\n            validateAccountInExternalService(dealerInstance)\r\n            checkIfInstanceWasConcurrentlyModified(dealerInstance, params.version)\r\n            dealerInstance.properties = params\r\n            saveUpdatedInstance(dealerInstance)\r\n            redirectToAfterUpdate(dealerInstance)\r\n        }\r\n    }\r\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"Grails controllers are not very DRY. It&#8217;s easy to find duplicated code fragments in default generated controller. Take a look at code sample below. It is duplicated four times in show, edit, update and delete actions:\nclass BookController {def show() {def bookInstance = Book.get(params.id)if (!bookInstance) {flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])redirect(action: \"list\")return        }        [bookInstance: bookInstance]    }}\nWhy is it duplicated?\nThere is a reason for that duplication, though. If you move this snippet to a method, it can redirect to \"list\" action, but it can&#8217;t prevent controller from further execution. After you call redirect, response status changes to 302, but after method exits, controller still runs subsequent code.\nSolution\nAt TouK we&#8217;ve implemented a simple trick to resolve that situation:\n\nwrap everything with a simple withStoppingOnRender method,\nwhenever you want to render or redirect AND stop controller execution &#8211; throw EndRenderingException.\n\nWe call it Big Return &#8211; return from a method and return from a controller at once. Here is how it works:\nclass BookController {def show(Long id) {withStoppingOnRender {Book bookInstance = Book.get(id)validateInstanceExists(bookInstance)            [bookInstance: bookInstance]        }    }protected Object withStoppingOnRender(Closure closure) {try {return closure.call()        } catch (EndRenderingException e) {}    }private void validateInstanceExists(Book instance) {if (!instance) {flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])redirect(action: \"list\")throw new EndRenderingException()        }    }}class EndRenderingException extends RuntimeException {}\nExample usage\nFor simple CRUD controllers, you can use this solution and create some BaseController class for your controllers. We use withStoppingOnRender in every controller so code doesn&#8217;t look like a spaghetti, we follow DRY principle and code is self-documented. Win-win-win! Here is a more complex example:\nclass DealerController {@Transactionaldef update() {withStoppingOnRender {Dealer dealerInstance = Dealer.get(params.id)validateInstanceExists(dealerInstance)validateAccountInExternalService(dealerInstance)checkIfInstanceWasConcurrentlyModified(dealerInstance, params.version)            dealerInstance.properties = paramssaveUpdatedInstance(dealerInstance)redirectToAfterUpdate(dealerInstance)        }    }}\n","protected":false},"author":37,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[50],"_links":{"self":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/11684"}],"collection":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/users\/37"}],"replies":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/comments?post=11684"}],"version-history":[{"count":3,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/11684\/revisions"}],"predecessor-version":[{"id":14867,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/11684\/revisions\/14867"}],"wp:attachment":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/media?parent=11684"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/categories?post=11684"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/tags?post=11684"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}