(Quick Reference)

1.1 What's new in Grails 2.4? - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari

Version: 2.4.4

1.1 What's new in Grails 2.4?

Groovy 2.3

Grails 2.4 comes with Groovy 2.3 which includes many new features and enhancements.

For more information on Groovy 2.3, see the comprehensive release notes.

Spring 4.0

Grails 2.4 comes with Spring 4.0.4 which includes many new features and enhancements. See the Spring documentation.

Hibernate 4.3

Grails 2.4 now uses Hibernate 4.3.5 by default (Hibernate 3 is still available as an optional install).

Standalone GORM and GSP

GORM and GSP can now be used outside of Grails. See the following guides / examples for more information:

The Asset-Pipeline replaces Resources to serve static assets.

The asset-pipeline provides a new, easier to manage, faster means of managing your JavaScript, CSS, and images, while also bringing compiled client languages in to the fray as first-class citizens (e.g. CoffeeScript, LESS, SASS).

All your assets should now live in the grails-app/assets subfolders. Three folders are made for you by default:

  • javascript
  • stylesheets
  • images

Now, defining manifests are done directly in your JavaScript files, or CSS by using require directives!

//= require jquery
//= require_self
//= require file_a
//= require_tree .

console.log('some javascript');

Easily add your assets to your GSP files:

<asset:javascript src="application.js"/>
<asset:stylesheet href="application.css"/>
<asset:image src="grails_logo.png" height="60" />

Enjoy developing with on the fly asset processing, asset compiling on WAR, and much more. See the docs for more info.

Static Compilation

Groovy is a dynamically dispatched, dynamically typed language by default but also has great support for static type checking and static compilation. See these notes on Groovy static compilation. In general, Grails supports Groovy's static compilation but there are a lot of special situations which are common in a Grails app which cannot be statically compiled. For example, if a method marked with @CompileStatic contains code which invokes a GORM dynamic finder the code will not compile because the Groovy compiler cannot verify that the dynamic finder is valid. Grails 2.4 improves on this by allowing code to be statically compiled and still do things like invoke GORM dynamic finders.

The grails.compiler.GrailsCompileStatic annotation behaves much like the groovy.transform.CompileStatic annotation and provides special handling to recognize Grails specific constructs.

The following controller is marked with @GrailsCompileStatic. All of the code that can be statically compiled will be statically compiled. When the compiler encounters code which can not be statically validated, normally that would result in a compile error. The Grails compiler will allow certain things to be considered valid and dynamically dispatch those instructions.

// grails-app/controllers/com/demo/PersonController.groovy
package com.demo

import grails.compiler.GrailsCompileStatic

@GrailsCompileStatic class PersonController {

def showKids() { def kids = Person.findAllByAgeLessThan(16)

// … } }

There may be situations where most of the code in a class should be statically compiled but a specific method should be left to dynamic compilation. See the following example.

import grails.compiler.GrailsCompileStatic
import groovy.transform.TypeCheckingMode

@GrailsCompileStatic class SomeClass {

def update() { // this method will be statically compiled }

@GrailsCompileStatic(TypeCheckingMode.SKIP) def save() { // this method will not be statically compiled }

def delete() { // this method will be statically compiled } }

The grails.compiler.GrailsTypeChecked annotation behaves much like the groovy.transform.TypeChecked annotation and provides special handling to recognize Grails specific constructs.

See the static compilation and type checking section for more details.

More Advanced Subqueries in GORM

The support for subqueries has been extended. You can now use in with nested subqueries:

def results = Person.where {
    firstName in where { age < 18 }.firstName
}.list()

Criteria and where queries can be seamlessly mixed:

def results = Person.withCriteria {
    notIn "firstName", Person.where { age < 18 }.firstName
}

Subqueries can be used with projections:

def results = Person.where {
    age > where { age > 18 }.avg('age')
}

Correlated queries that span two domain classes can be used:

def employees = Employee.where {
    region.continent in ['APAC', "EMEA"]
}.id()

def results = Sale.where { employee in employees && total > 100000 }.employee.list()

And support for aliases (cross query references) using simple variable declarations has been added to where queries:

def query = Employee.where {
    def em1 = Employee
    exists Sale.where {
        def s1 = Sale
        def em2 = employee
        return em2.id == em1.id
    }.id()
}
def results = query.list()

GORM for Hibernate in Unit tests

It is no longer necessary to create integration tests in order to test GORM interactions with Hibernate. You can now instead use HibernateTestMixin:

import grails.test.mixin.TestMixin
import grails.test.mixin.gorm.Domain
import grails.test.mixin.hibernate.HibernateTestMixin
import spock.lang.Specification

@Domain(Person) @TestMixin(HibernateTestMixin) class PersonSpec extends Specification {

void "Test count people"() { expect: "Test execute Hibernate count query" Person.count() == 0 sessionFactory != null transactionManager != null session != null } }

This library dependency is required in grails-app/conf/BuildConfig.groovy for adding support for HibernateTestMixin

dependencies {
        test "org.grails:grails-datastore-test-support:1.0-grails-2.4"
    }

HibernateTestMixin is only supported with hibernate4 plugin versions >= 4.3.5.4 .

plugins {
        runtime ':hibernate4:4.3.5.4'
    }

Views For Namespaced Controllers

The views for namespaced controllers may now be defined in the grails-app/views/<namespace name>/<controller name>/ directory. See the Models And Views section for more details.

Improved Programmatic Transactions

Transaction attributes may now be specified when invoking withTransaction.

// the keys in the Map must correspond to properties
// of org.springframework.transaction.support.DefaultTransactionDefinition

Account.withTransaction([propagationBehavior: TransactionDefinition.PROPAGATION_REQUIRES_NEW, isolationLevel: TransactionDefinition.ISOLATION_REPEATABLE_READ]) { // … }

See the withTransaction docs for more information.

New Maven Plugin

The Maven plugin has been rewritten to use Aether for dependency resolution and can now be used with both Grails 2.3.x and Grails 2.4.x without releasing a new version of the plugin.

This means that the Maven plugin version number is no longer tied to the version number of Grails and new releases of the Maven plugin will not come out with each new Grails release. Instead, users can continue to use the 2.4.0 version of the plugin for any version of Grails going forward.

Unit Testing improvements

There is a Grails "unit testing runtime" that is based on the previous TestMixin based solution. It now separates the TestMixin classes and the actual runtime that handles the lifecycle of the Grails unit testing runtime. State of the runtime is not kept in static fields of the TestMixin classes anymore. The Groovy AST transformation behind the TestMixin annotation integrates to JUnit and Spock test classes by adding JUnit Rule fields to the class. In the previous solution, Before/BeforeClass and After/AfterClass annotations on AST added mix-in methods were used for the integration.

Some of the main features:

  • The programming model remains the same for unit testing of Grails applications
  • Setup/teardown method ordering is now deterministic because the integration is now using a single JUnit Rule field and the test runtime uses eventing internally to setup and teardown resources
  • There are doWithSpring and doWithConfig callbacks for unit tests - these callback methods get called before the grailsApplication instance in the unit test runtime gets initialized.
  • It's possible to register a Spock Mock as a bean to the application context of the Grails unit test runtime application - you can replace a collaborator bean with a mock
  • It's possible to reuse a single application context for several test classes and control that so that tests can be made faster when required
  • The Grails unit testing runtime has an event-based plugin architecture. It's possible to add new test runtime "features" with new test runtime plugin classes. The test runtime plugin API is due to change. Changes will be made based on feedback from the Grails community. The main interfaces of the API are currently documented in the javadocs: TestPlugin, TestEventInterceptor and TestEvent. Custom test plugins are currently limited since there isn't a solution for scanning for available test plugins. It's now possible to add custom test plugins in a static initialization block of a test class by calling TestRuntimeFactory.addPluginClass .

See the updated unit testing chapter in the manual for more information of the new features like doWithSpring and doWithConfig.

Improved Unit Testing Support For allowedMethods

The allowedMethods property is now respected in unit tests.

// grails-app/controllers/com/demo/DemoController.groovypackage com.demo

class DemoController {

static allowedMethods = [save: 'POST', update: 'PUT', delete: 'DELETE']

def save() { render 'Save was successful!' }

// … }

// test/unit/com/demo/DemoControllerSpec.groovy
package com.demo

import grails.test.mixin.TestFor import spock.lang.Specification import static javax.servlet.http.HttpServletResponse.*

@TestFor(DemoController) class DemoControllerSpec extends Specification {

void "test a valid request method"() { when: request.method = 'POST' controller.save()

then: response.status == SC_OK response.text == 'Save was successful!' }

void "test an invalid request method"() { when: request.method = 'DELETE' controller.save()

then: response.status == SC_METHOD_NOT_ALLOWED } }