miércoles, 26 de septiembre de 2012

Entendiendo el Plugin Resources

Este plugin representa una nueva forma de declarar tus recursos estáticos (css, js e img) mediante módulos y lincarlos a tus GSP por medio de tags específicas.
Primero, instala el plugin resources. Si tienes instalado Grails 2 ya viene por defecto instalado. Otros plugins muy interesantes para optimizar el rendimiento de tu aplicación serían: zipped-resources, cached-resources, lesscss-resources, gsp-resources, yui-minify-resources, handlebars-resources y page-resources.
Empezaremos declarando los módulos en el fichero "AppResources.groovy" que está en el directorio conf
modules = {
 common {
  resource url:"css/main.css"
  resource url:"js/jquery.js"
 }
}

Aquí le estamos diciendo que declare un módulo llamado "common" con dos recursos: main.css y jquery.js en donde por defecto los css los pondrá en el head y el js al final de la GSP (“defer”) que le definamos. Si queremos que los js los ponga en el head de la GSP, le añadiremos a la linea del resource disposition:"head"
Ahora necesitas meterlo dentro de una GSP. Para hacer esto, necesitas las tags propias del Plugin. Antes de nada, definamos el layout Sitemesh de nuestra aplicación.
<html>
    <head>
        
        
    </head>
    <body>
        
</body> </html>

El primer r:layoutResources servirá para lincar todos los css, js, iconos, etc que tengas en tu <head> y el segundo para renderizar algún resource “defer”, que normalmente es sólo código JavaScript.

Para utilizar un módulo de recursos en una GSP:
<html>
    <head>
        
        
    </head>
    <body>
    …
    </body>
</html>

A veces tienes que controlar el orden de tus recursos ya que dependen de otros para cargarse bien. La regla general de este plugin es que se empiecen a cargar los recursos en orden de definición (es decir de arriba a abajo). También se puede hacer de otra manera, con "dependsOn". Por ejemplo,
modules = {
    customize{
        dependsOn "common"
        resource url:'css/customize.css'
    }
}

Con esto le estamos diciendo que antes de cargar el módulo customize, debe estar cargado el módulo common.
Otra de las tags que pueden ser útiles es la r:script. Esta tag se utiliza para introducir algo de código JS en la GSP.
<html>
    <head>
        
        
        window.alert('Boo!')
    </head>
    <body>
    …
    </body>
</html>

Este alert que hemos definido se cargará con disposition "defer", es decir se cargará cuando se haya cargado toda la página, aunque esté definido en el head.
Si quisiéramos forzar a que este trozo de JS se cargara en el head le tendríamos que modificar la tag con,
<r:script disposition="head" >

Otro tipo de recurso que se puede utilizar en este plugin son las imágenes. Se puede hacer de dos formas: llamándole desde la GSP,
<r:img uri="images/logo.png"/>

o creando un módulo desde AppResources,
modules = {
    images {
        resource url:'images/logo.png',
            attrs:[width:400, height:180, alt:'ACME Corp.'],
            disposition: 'image'
    }
}

lunes, 24 de septiembre de 2012

Chain = encadenar

Las acciones pueden ser encadenadas. El encadenamiento permite guardar el model de una acción a otra. Por ejemplo llamamos a sucesivas acciones en este código:
class ExampleChainController {

def first() { chain(action: second, model: [one: 1]) }

def second () { chain(action: third, model: [two: 2]) }

def third() { [three: 3]) } 
}

Al final de la tercera accion tu model sería,
[one: 1, two: 2, three: 3]

El modelo puede ser accedido en el siguiente controller al chain usando el mapa chainModel. Esta propiedad dinámica existe sólo en acciones siguientes a la llamada de algún método chain.
class ChainController {

def nextInChain() { 
def model = chainModel.myModel
 … 
} 
}

Como en el método redirect, puedes pasar también parámetros a un método chain:
chain(action: "action1", model: [one: 1], params: [myparam: "param1"])

Peticiones asíncronas

Grails soporta el procesamiento de peticiones asícronas como característica de la especificación de Servlet 3.0. Para activar la característica necesitas añadir la siguiente líena de código en tu BuildConfig.groovy:
grails.servlet.version = "3.0"

Con esto te aseguras de que tienes la caracterísitica activada en tiempo de compilación.
NOTA: Con el Servlet 3.0 activado, podrás solamente hacer deploy en contenedores que lo soporten como Tomcat 7, etc.
- Renderizar asíncronamente
Puedes renderizar contenido (templates, binary data, etc) de una forma asíncrona por medio de una llamada al método startAsync que devuelve una instancia del AsyncContext de Servlet 3.0. Una vez que tienes una referencia al AsyncContext, puedes usar el método render:
def index() {
    def ctx = startAsync()
    ctx.start {
        new Book(title:"The Stand").save()
        render template:"books", model:[books:Book.list()]
        ctx.complete()
    }
}

Deberías llamar al método complete() para terminar la conexión.

- Reanudar una petición asíncrona
Para reanudar el procesamiento de una petición asíncrona (por ejemplo para delegar a una vista renderizada), se usa el método dispatch de la clase AsyncContext:
def index() {
    def ctx = startAsync()
    ctx.start {
        // do working
        …
        // render view
        ctx.dispatch()
    }
}

viernes, 21 de septiembre de 2012

Interceptores en controladores

A menudo es útil para interceptar procesamientos de cualquier petición, sesión o estado de aplicación. Esto puedes lograrlo con la acción de los interceptores. Actualmente hay dos tipos de interceptores: before and after.

NOTA: Si tu interceptor se aplica a más de un controlador, deberías definir mejor un Filtro. Los filtros pueden aplicarse a múltiples controladores o a URIs sin la necesidad de cambiar la lógica de cada controlador.

- Before Interception
Intercepta procesamientos antes de que la acción se ejecute. Si devuelve false, la acción interceptada no será ejecutada. El interceptor podrá ser definido en todas las acciones de un controlador como lo siguiente:
def beforeInterceptor = {
    println "Tracing action ${actionUri}"
}

El código es declarado en el cuerpo del controlador. Será ejecutado antes que todas las acciones y no interferirá en el procesamiento. Un caso común es una simple autenticación:
def beforeInterceptor = [action: this.&auth, except: 'login']

// defined with private scope, so it's not considered an action 
private auth() { 
if (!session.user) { redirect(action: 'login') return false } }
def login() { // display login page }

El código define un método llamado auth. Un método privado es usado para no exponerlo como acción fuera de todo. El beforeInterceptor define un interceptor que es usado para todas las acciones excepto en la acción login y que ejecuta el método auth. El método auth se referencia usando la sintaxis de puntero de un método Groovy.
Dentro del método detecta si hay un usuario en sesión, si no es así redirige a la acción de login y devuelve un false, causando que la acción interceptada no sea procesada.

- After Interception
Usa la propiedad afterInterceptor para definir un interceptor que es ejecutado después de una acción:
def afterInterceptor = { model ->
    println "Tracing action ${actionUri}"
}

El afterInterceptor toma el resultado del model como argumento y por lo tanto puede manipular el modelo o la respuesta.
Un afterInterceptor puede también modificar el objeto ModelAndView de Spring MVC antes de renderizarlo. E este caso, sería:
def afterInterceptor = { model, modelAndView ->
    println "Current view is ${modelAndView.viewName}"
    if (model.someVar) modelAndView.viewName = "/mycontroller/someotherview"
    println "View is now ${modelAndView.viewName}"
}

Esto permite que la vista sea cambiada basándose en el modelo devuelto por la actual acción. Anotar que el modelAndView puede ser null si la acción que ha sido interceptada llama a un redirect o un render.

Ámbito en los controladores

Al igual que los servicios, nosotros podemos fijar el scope también en los controladores. Por defecto, una instancia del controlador es creada por cada petición. De hecho, todos los controladores por defecto son de ámbito "prototype", es thread-safe que quiere decir que cada petición ocurre en su propio thread.

Puedes cambiar este comportamiento de los controladores sustituyendo por uno de los ámbitos soportados:
- prototype (default): un nuevo controlador será creado por cada petición (recomendado para acciones como propiedades de closure)
- session: un controlador es creado por cada ámbito de sesión
- singleton: solamente una instancia del controlador existe (recomendado para acciones como los métodos)

Para activar uno de los ámbitos, se añade una propiedad estática llamada scope en tu controlador con uno de los ámbitos válidos, cmo por ejemplo,
static scope = "singleton"

Puedes definir la estrategia por defecto en el fichero Config.groovy con la clave grails.controllers.defaultScope, por ejemplo:
grails.controllers.defaultScope = "singleton"

NOTA: Usar los ámbitos en los controladores sabiamente. Por instancia, no recomendamos tener ninguna propiedad en un controlador singleton-scoped ya que serán compartidos por todas las peticiones. Poniendo el ámbito por defecto que no sea prototype también puede dar lugar a comportamientos inesperados si tienes controladores suministrados por plugins instalados ya que se esperan que el ámbito sea prototype.

¿Por qué métodos en lugar de closures?

En versiones anteriores de Grails, las acciones en los controladores eran implementadas con closures. Esto en Grails 2 todavía está soportado, pero la opción preferida es usar métodos.

Elegir métodos en vez de closures tiene algunas ventajas:
- Memoria eficiente
- Permite uso de de controladores en singleton scope
- Puedes sobrescribir acciones de subclases y llamar al método de la superclase con super.actionName()
- Los métodos pueden ser interceptados con mecanismos estandar de proxying, algo que es complicado de hacer con closures ya que tienen campos

Si prefieres la sintaxis con closures o tienes un controlador antiguo creado en una versión anterior de Grails y quieres las ventajas de usar métodos, puedes poner en el BuildConfig.groovy la siguiente linea,

grails.compile.artefacts.closures.convert=true
y en tiempo de compilación las transformaciones AST convertirán tus closures en métodos en el bytecode generado.

NOTA: Si un controlador extiende de otra clase que no está definida dentro del directorio app/controllers/, lo métodos heredados de esa clase no serán conertidos en acciones. Si la intención es exponer esos métodos heredados como acciones de un controlador, los métodos pueden ser sobreescritos en la subclase y el método de la subclase puede invocar al método en la super clase.