Todavía no he tenido oportunidad(¿tiempo?) de probar las betas de la versión 1.1 de Grails, pero ya llevo unas semanas siguiendo un poco las novedades que están llegando con las tres betas que se han publicado, tanto con las relesae notes como en la lista de correo.
Hay cosas que me parecen muy interesantes: el trabajo que se ha hecho en cuanto infraestructura, el manejo de submits duplicados, el "nuevo" framework de testing, las mejoras para el desarrollo de plugins, poder usar GORM fuera de Grails...
Estas son las principales novedades:
GORM:
GORM es standalone, ya se puede usar fuera de Grails.
Se completan los eventos de GORM con afterInsert, afterUpdate y afterDelete.
Persistencia de colecciones de tipos básicos, como colecciones de String:
class Person {
static hasMany = [nicknames:String]
}
Persistencia de coleciones de tipos enum:
enum VehicleStatus { OFF, IDLING, ACCELERATING, DECELARATING }
class Truck {
static hasMany = [statuses:VehicleStatus]
}
Acceder a objetos persistentes en modo sólo lectura, buscándolos por id:
def book = Book.read(1)
Ordenación por defecto a nivel de clase y en las asociaciones:
class Book {
String title
static mapping = {
sort "title"
}
}
class Author {
static hasMany = [books:Book]
static mapping = {
books sort:"title"
}
}
Obtención por lotes a nivel de clase y en asociaciones:
class Book {
String title
static mapping = {
batchSize 15
}
}
Mejoras en los finders dinámicos. Se podrá utilizar el sufijo InList, cachear queries y usar bloqueo pesismista:
def books = Book.findByAuthorInList(['Dierk Koenig', 'Graeme Rocher'])
def book = Book.findByTitle("Groovy in Action", [cache:true] )
def book = Book.findByTitle("Groovy in Action", [lock:true] )
Relaciones uno a muchos unidireccionales con mapeo heredado, usando joinTable:
class Book {
String title
static belongsTo = Author
static hasMany = [authors:Author]
static mapping = { authors joinTable:[name:"mm_author_books", key:'mm_book_id' ] }
}
class Author {
String name
static hasMany = [books:Book]
static mapping = { books joinTable:[name:"mm_author_books", key:'mm_author_id'] }
}
Los tipos Enum pueden especificar un método getId() que GORM llamará para persistir el estado del enum
enum Country {
AUSTRIA('at'),
UNITED_STATES('us'),
GERMANY('de');
final String id
Country(String id) { this.id = id } }
Plugins:
Plugins globales, para instalar en todas las aplicaciones
grails install-plugin webtest -global
Múltiples repositorios de plugins:
grails.plugin.repos.discovery.myRepository="http://svn.codehaus.org/grails/trunk/grails-test-plugin-repo"
grails.plugin.repos.distribution.myRepository="https://svn.codehaus.org/grails/trunk/grails-test-plugin-repo"
Resolución transitiva de depndencias entre plugins.
Se puede definir un ámbito y el entorno donde un plugin se cargará o empaquetará
def environments = ['dev', 'test']
def scopes = [excludes:'war']
Desarrollo modular de aplicaciones usando plugins, sin necesidad de instalarlos y pudiendo estar en cualquier lugar del sistema de ficheros. Muy útil para probar un plugin propio en una aplicación real(esto antes resultaba incómodo), o para unir en una aplicación funcionalidades repartidas en plugins.
Testing:
Se ha añadido testing plugin como framework de tests que mejora el de las versiones 1.0.x
Data Binding:
Se pueden definir de qué propiedades se hace el binding:
person.properties["firstName","lastName"] = params
Es posible hacer binding de colecciones de objetos, Grails se encarga de instanciar los objetos y los índices:
<g:textField name="books[0].title" value="the Stand" />
<g:textField name="books[1].title" value="the Shining" />
<g:textField name="books[2].title" value="Red Madder" />
Scaffolding:
El scaffolding dinámico también generará las vistas usando los templates generados(y personalizados) con install-templates.
El scaffolding ahora soporta relaciones muchos a muchos y asociaciones uno a muchos unidireccionales.
Controllers:
Manejo de submits duplicados o invalidos usando el patrón Synchronizer Token:
//en el gsp
<g:form useToken="true">...</g:form>
//controlador
withForm {
// good request
}.invalidToken {
// bad request
}
Además de redireccionar ya es posible hacer forward de una petición:
forward controller:"home", action:"index"
Groovy Server Pages:
Ya es posible utilizar librerías de tags JSP, de igual forma que cualquier librería de tags GSP:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:formatNumber value="${10}" pattern=".00"/>
${fmt.formatNumber(value:10, pattern:".00")}
Un namespace propio para usar templates, tmpl:
//template _tableRow.gsp
<tr>
<td class="prop">${label}</td>
<td class="value">${value}</td>
</tr>
//Otro gsp
<tmpl:tableRow label="one" value="two" />
Es posible hacer includes de la respuesta de otro controlador, acción o vista en la vista actual:
<g:include controller="book" action="list"/>
Posibilidad de renderizar templates de los plugins:
<g:render template="pathToTemplateFile" plugin="miplugin"/>
Ahora los gsp se renderizan más rápido, evitando el uso de StringBuffer y StringWriter.
Infraestructura:
Grails viene con un plugin de Maven y un arquetipo:
mvn grails:create-pom
Integración con Ivy y Ant, crea el build.xml y el ivy.xml para construir la aplicación Grails sin que sea necesario que Grails esté instalado en el servidor.
BeanBuilder (el DSL de Spring), ahora soporta los namespaces de Spring pudiendo utilizar así el soporte de AOP.
Nuevo API para acceder al entorno de ejecución y a los meta datos de la aplicación:
switch(Environment.current) {
case Environment.DEVELOPMENT:
configureForDevelopment()
break
case Environment.PRODUCTION:
configureForProduction()
break
}
def metadata = grails.util.Metadata.current
println metadata.applicationName
println metadata.applicationVersion
Nuevo DSL para configurar Log4j
La configuración de los builds se ha flexibilizado pudiendo configurar diferentes aspectos en el nuevo BuildConfig
Posibilidad de encriptar las contraseñas del DataSource
--
Veremos qué añadidos/mejoras más aparecen en las RC y hasta que esté disponible Grails 1.1