Search code examples
grailsgroovyclosurescriteria

Grails: Use several closures inside another one


I am creating a criteria depending on several factors. Currently I'm using nested closures in the following way:

Utils.groovy

static def statusAndOrderCriteria = { x ->
    ...
    Utils.commonOrderCriteria.delegate = delegate
    Utils.commonOrderCriteria(x)
}

static def commonOrderCriteria = { x ->
    ...
}

Usage:

Alert.createCriteria().list({
    Utils.statusAndOrderCriteria.delegate = delegate
    Utils.statusAndOrderCriteria(x)
})

I want to use several closures, such as:

Alert.createCriteria().list({
    Utils.firstCriteria(a)
    Utils.secondCriteria(b)
    Utils.statusAndOrderCriteria(x)
})

I don't know how to achieve that. I know that I can't be overwriting the delegate object several times on the same closure.


Solution

  • When you build a GORM criteria query, the delegate of the closure is an instance of HibernateCriteriaBuilder. What you can do is modify your Util closures to accept this builder as a parameter. Then, delegate the method calls to the builder. Here's an example:

    class Utils {
        static def statusAndOrderCriteria = { criteriaBuilder, x ->
            criteriaBuilder.eq('foo', x)
            Utils.commonOrderCriteria(criteriaBuilder, x)
        }
    
        static def commonOrderCriteria = { criteriaBuilder, x ->
            criteriaBuilder.lt('bar', x)
        }
    }
    

    Then you can use the static closures in Util like this:

    Alert.createCriteria().list({
        Utils.firstCriteria(delegate, a)
        Utils.secondCriteria(delegate, b)
        Utils.statusAndOrderCriteria(delegate, x)
    })
    

    Hint: If the closures were not static properties you'd be able to avoid constantly having to pass the delegate, like this:

    @groovy.transform.TupleConstructor
    class Utils {
        HibernateCriteriaBuilder criteriaBuilder
    
        def statusAndOrderCriteria = { x ->
            criteriaBuilder.foo()
            Utils.commonOrderCriteria(x)
        }
    
        def commonOrderCriteria = { x ->
            criteriaBuilder.bar()
        }
    }
    
    Alert.createCriteria().list({
        def utils = new Utils(delegate)
    
        utils.firstCriteria(a)
        utils.secondCriteria(b)
        utils.statusAndOrderCriteria(x)
    })