nunogt
nunogt

Reputation: 168

What is the idiomatic Groovy and Grails way of casting to a Domain Model class?

Given the Grails Domain Classes "Apple.groovy" and "Orange.groovy", not strictly related, and the following Grails Service invoked from the Controllers:

package test

import grails.transaction.Transactional

import javax.servlet.http.HttpServletRequest

@Transactional
class UploadService {

    def uploadApple(HttpServletRequest request, Apple o) {
        def file = request.getFile('payload')
        o.payload = file.getBytes()
        o.filename = file.originalFilename
        o.contentType = file.contentType
        o.save(flush:true)
    }

    def uploadOrange(HttpServletRequest request, Orange o) {
        def file = request.getFile('payload')
        o.payload = file.getBytes()
        o.filename = file.originalFilename
        o.contentType = file.contentType
        o.save(flush:true)
    }

}

How would one go about unifying this code under a common method? I was hoping Groovy's optional types would handle this for me, but I can't seem to be able invoke .save() successfully if I remove the types from the method signature.

Thanks in advance

Upvotes: 0

Views: 529

Answers (2)

Armaiti
Armaiti

Reputation: 766

Avoid passing request to service and pass File to service. Did you try it this way?

def upload(file, obj) {
        obj.payload = file.getBytes()
        obj.filename = file.originalFilename
        obj.contentType = file.contentType
        obj.save(flush:true)
}

Unlike Java, Groovy does duck typing. Typing is concerned with assigning a type to any object. Duck typing is concerned with establishing the suitability of an object for some purpose.

Now you have to be careful because this will not work for every object. The reason that it works for Apple and Orange is because both have exactly the same set of attributes and attribute types.

One would wonder, why you would have two different domain that behave exactly the same, but that's obviously is a different discussion.

Upvotes: 3

Emmanuel Rosa
Emmanuel Rosa

Reputation: 9885

In Grails 3, domain classes implement the GormEntity trait, which is where the save() method comes from. But that doesn't solve the issue with the payload, filename, and contentType properties.

What you can do is create an interface which declares the methods and properties common to both domain classes and also implements org.grails.datastore.gorm.GormEntity. Then, have the domain classes implement the interface:

interface Uploadable extends GormEntity {
    byte [] payload
    String filename
    String contentType
}

class Apple implements Uploadable {
    byte [] payload
    String filename
    String contentType
}

Then, you can use the interface in your service.

@Transactional
class UploadService {

    def upload(HttpServletRequest request, Uploadable o) {
        def file = request.getFile('payload')
        o.payload = file.getBytes()
        o.filename = file.originalFilename
        o.contentType = file.contentType
        o.save(flush:true)
    }

}

Note: Since Grails injects the GormEntity trait into the domain classes automatically, I don't know what happens if you use it explicitly as shown.

Upvotes: 2

Related Questions