idiom for save and update methods in grails - grails

Are there in any idioms in grails which help us with saving domain objects ?
For example
i may want to do something like
if(candidate.hasErrors || !candidate.save)
{
candidate.errors.each {
log it
}
However i do not want to spread the logic across all the places i do domainObject.save.
I also do not want seperate class like say repo to which I pass this domainObject and put in this logic
Thanks
Sudarshan

Here's a service method that I've used to validate and save, but log resolved validation messages on failure. It's helpful to use this instead of just println error or log.warn error since the toString() for error objects is very verbose and you just want to see what would be displayed on the GSP:
class MyService {
def messageSource
def saveOrUpdate(bean, flush = false) {
return validate(bean) ? bean.save(flush: flush) : null
}
boolean validate(bean) {
bean.validate()
if (bean.hasErrors()) {
if (log.isEnabledFor(Level.WARN)) {
def message = new StringBuilder(
"problem ${bean.id ? 'updating' : 'creating'} ${bean.getClass().simpleName}: $bean")
def locale = Locale.getDefault()
for (fieldErrors in bean.errors) {
for (error in fieldErrors.allErrors) {
message.append("\n\t")
message.append(messageSource.getMessage(error, locale))
}
}
log.warn message
}
bean.discard()
return false
}
return true
}
And here's an example in a controller:
class MyController {
def myService
def actionName = {
def thing = new Thing(params)
if (myService.saveOrUpdate(thing)) {
redirect action: 'show', id: thing.id
}
else {
render view: 'create', model: [thing: thing]
}
}
}
Edit: It's also possible to add these methods to the MetaClass, e.g. in BootStrap.groovy:
class BootStrap {
def grailsApplication
def messageSource
def init = { servletContext ->
for (dc in grailsApplication.domainClasses) {
dc.metaClass.saveOrUpdate = { boolean flush = false ->
validateWithWarnings() ? delegate.save(flush: flush) : null
}
dc.metaClass.validateWithWarnings = { ->
delegate.validate()
if (delegate.hasErrors()) {
def message = new StringBuilder(
"problem ${delegate.id ? 'updating' : 'creating'} ${delegate.getClass().simpleName}: $delegate")
def locale = Locale.getDefault()
for (fieldErrors in delegate.errors) {
for (error in fieldErrors.allErrors) {
message.append("\n\t")
message.append(messageSource.getMessage(error, locale))
}
}
log.warn message
delegate.discard()
return false
}
return true
}
}
}
}
This depends on a 'log' variable being in scope, which will be true in any Grails artifact. This changes the controller usage slightly:
class MyController {
def actionName = {
def thing = new Thing(params)
if (thing.saveOrUpdate()) {
redirect action: 'show', id: thing.id
}
else {
render view: 'create', model: [thing: thing]
}
}
}
As a metaclass method it may make more sense to rename it, e.g. saveWithWarnings().

Related

How to write integration test for filter in grails

I have written one filter rule which I want to test using grails integration tests.
Filter is
invalidAccess(controller: "home") {
before = {
redirect(controller: "newHome", action: "index")
return false
}
}
I have followed this link to write the integration test
http://ldaley.com/post/392153102/integration-testing-grails-filters
It returns result as false But gives
null for redirectedUrl
instead of newHome & index method url.
What am I missing here?
import grails.util.GrailsWebUtil
class MyFilterTests extends GroovyTestCase {
def filterInterceptor
def grailsApplication
def grailsWebRequest
def request(Map params, controllerName, actionName) {
grailsWebRequest = GrailsWebUtil.bindMockWebRequest(grailsApplication.mainContext)
grailsWebRequest.params.putAll(params)
grailsWebRequest.controllerName = controllerName
grailsWebRequest.actionName = actionName
filterInterceptor.preHandle(grailsWebRequest.request, grailsWebRequest.response, null)
}
def getResponse() {
grailsWebRequest.currentResponse
}
def testFilterRedirects() {
def result = request( someParameter: "2", "home", "index")
assertFalse result
assertTrue response.redirectedUrl.endsWith(/* something */)
}
}
If you want to try unit testing and need to mock some services then you can mock like:
#TestFor(SomethingToTest)
#Mock([FirstService, SecondService])
class SomethingToTestSpec extends Specification {
and you want integration test then try following test
import grails.util.GrailsWebUtil
import org.junit.After
import org.junit.Before
import org.junit.Test
class MyFilterIntegrationTests {
def filterInterceptor
def grailsApplication
def grailsWebRequest
#Before
void setUp() {
}
#After
void tearDown() {
}
#Test
void testFilterRedirects() {
def result = request("person", "index", someParameter: "2")
assert !result
assert response.redirectedUrl.endsWith('/auth/index')
}
def getResponse() {
grailsWebRequest.currentResponse
}
def request(Map params, controllerName, actionName) {
grailsWebRequest = GrailsWebUtil.bindMockWebRequest(grailsApplication.mainContext)
grailsWebRequest.params.putAll(params)
grailsWebRequest.controllerName = controllerName
grailsWebRequest.actionName = actionName
filterInterceptor.preHandle(grailsWebRequest.request, grailsWebRequest.response, null)
}
}
Ref# Grails Integration Test Filter

Domain Multiple inheritance and manyToMany relation removeFrom not working

I prepared some complex model structure due to content management businnes.
My main content model likes as following code.
class CmsContent implements Comparable<CmsContent>, Taggable, Serializable {
Set<CmsContent> contents
static hasMany = [contents:CmsContent]
}
Other Content Model extends from above
class Menu extends CmsContent {
}
class Image extends CmsContent {
}
class Video extends CmsContent {
}
On Controller side when ever add Image to Menu it is perfectly working
def addContent(){
Menu menuInstance = Menu.get(params.id)
if (menuInstance == null) {
notFound()
return
}
CmsContent content = CmsContent.get(params.contentId)
if (content == null) {
notFound()
return
}
menuInstance.addToContents(content)
menuInstance.save(flush: true)
request.withFormat {
'*'{
def result =[:] ;
result.status ='success';
render result as JSON
}
}
}
def removeContent(){
Menu menuInstance = Menu.get(params.id)
if (menuInstance == null) {
notFound()
return
}
long id = Long.valueOf(params.contentId)
CmsContent content = cmsContent.contents.find { it.id == id }
if (content == null) {
notFound()
return
}
menuInstance.removeFromContents(content)
menuInstance.save(flush: true)
request.withFormat {
'*'{
def result =[:] ;
result.status ='success';
render result as JSON
}
}
}
But when I tried to remove facing following exception.
Cannot get property 'name' on null object. Stacktrace follows:
Message: Cannot get property 'name' on null object
I tracked down in grails code in DomainClassGrailsPlugin.
I realize that addTo Method and line 368 checking prop.otherSide attribute
if (prop.bidirectional && prop.otherSide) {
}
unfortunately removeTo method not check prop.otherSide attribute, therefore name field throwing the exception.
if (prop.bidirectional) {
if (prop.manyToMany) {
String name = prop.otherSide.name
arg[name]?.remove(delegate)
} else {
arg[prop.otherSide.name] = null
}
}
What do you think ? Do you agree with me ?

How can I create a map with all i18n-messages in Grails

I need this to render a part of it in a controller like:
class MessageController {
def index = {
def messageMap = listAlli18nMessages() // the question
render (contentType: "text/xml") {
messageMap {key, message ->
..
}
}
}
}
Finally I found an answer - override the default Grails messageSource:
class ExtendedPluginAwareResourceBundleMessageSource extends PluginAwareResourceBundleMessageSource {
Map<String, String> listMessageCodes(Locale locale) {
Properties properties = getMergedProperties(locale).properties
Properties pluginProperties = getMergedPluginProperties(locale).properties
return properties.plus(pluginProperties)
}
}
In grails-app/conf/spring/resources.groovy:
beans = {
messageSource(ExtendedPluginAwareResourceBundleMessageSource) {
basenames = "WEB-INF/grails-app/i18n/messages"
}
}
Corresponding controller code:
class MessageController {
def messageSource
def index = {
def messageMap = messageSource.listMessageCodes(request.locale)
render (contentType: "text/xml") {
messageMap {key, message ->
..
}
}
}
}
The approach you are taking doesn't look to be possible based on the API docs for PluginAwareResourceBundleMessageSource. This will get you close to a solution
class MessageController {
def messageSource
def index = {
Locale locale = new Locale('en');
List codes = ['default.paginate.prev','default.paginate.next','default.boolean.true','default.boolean.false']
def messageMap = messagesForCodes(codes,locale)
render (contentType: "text/xml") {
messageMap {key, message ->
..
}
}
}
private def messagesForCodes(codes, locale){
Map messages = [:]
codes.each{code->
messages[code] = messageSource.getMessage(code,null,locale)
}
messages
}
}

Does using #Transactional disable the grails default transaction management

According to the grails docs, services are transactional by default. But, I know you can get more fine grained control of transactions by using the Transactional attribute.
If I have a service such as
class MyService {
#Transactional(...config...)
def method1() { }
def method2() { }
}
My understanding is that in this case, method1 will be transactional, but method2 will not.
If I have
class MyService {
def method1() { }
def method2() { }
}
Then both method1 and method2 will both be transactional.
Is this correct?
If you want your service as transactional set to true the transactional property (this isn't obligatory but if you want to make clear that the service is transactional):
class MyService {
static transactional = true
def method1() { }
def method2() { }
}
If you don't want to:
class MyService {
static transactional = false
#Transactional(...config...)
def method1() { }
def method2() { }
}
Another example (setting transactional property isn't obligatory, but helps to be clear - if you are not the only coding this):
import org.springframework.transaction.annotation.Transactional
class BookService {
#Transactional(readOnly = true)
def listBooks() {
Book.list()
}
#Transactional
def updateBook() {
// …
}
def deleteBook() {
// …
}
}
Another thing you can do is annotate the whole class and override the methods you need to be different:
import org.springframework.transaction.annotation.Transactional
#Transactional
class BookService {
#Transactional(readOnly = true)
def listBooks() {
Book.list()
}
def updateBook() {
// …
}
def deleteBook() {
// …
}
}
Hope this helps ;)
You can disable the Grails default transaction management using withTransaction closure for domains to manage your Transaction manually as follows:
Account.withTransaction { status ->
try {
//write your code or business logic here
} catch (Exception e) {
status.setRollbackOnly()
}
}
If an exception is thrown, then the Transaction will be rollbacked.

Call namedQuery inside a criteria in controller

Is possible to call namedQuery on grails inside a controller? I know that I can call a namedQuery inside another namedQuery, but i dont want to do that. Any ideas? Thanks
User.groovy
static namedQueries = {
filterUsers{
eq("age", 21)
}
}
MyController.groovy
def r = User.createCriteria().list {
eq("id", 1)
filterUsers() //not possible
}
or..
MyController.groovy
//not possible too
//Cannot invoke method createCriteria() on null object
def r = User.filterUsers().createCriteria().list {
eq("id", 1)
}
Here's an example:
Domain:
class User {
int age
String userName
static namedQueries = {
filterUsers {
eq("age", 21)
}
}
static constraints = {
}
}
Controller:
class TestController {
def index = {
def users = User.filterUsers {
and {
like 'userName', 'Derek%'
}
}
render users as JSON
}
}
Also, you can find more about this here: Reference Documentation

Resources