GROOVY

INTRODUCCIÓN A GROOVY
1. Groovy vs Java
SIMILITUDES
- Palabras reservadas y sentencias
- try / catch / finally para la gestión de excepciones
- Definiciones de clase, interface, campos y métodos
- Instanciación de objetos mediante el operador new
- Empaquetado e importación
- Operadores, expresiones y asignaciones
- Estructuras de control
- Comentarios
- Comparten en mismo modelo de datos y nivel de ejecución de Java: JVM

OPCIONAL
- Paréntesis
- Sentencias return
- Punto y coma al final de las sentencias
- Algunas sentencias import Groovy ya las incluye automáticamente
groovy.lang.*, groovy.util.*, java.lang.*, java.util.*, java.util.regex.*, java.net.*, java.io.*, java.math.BigDecimal, java.math.BigInteger

DIFERENCIAS
- Closures (bloques de código)
- Soporte avanzado para cadenas de caracteres (GString), expresiones regulares y generación de plantillas
- Orientación a objetos real
- Sobrecarga de operadores y estructuras sintácticas de apoyo a las clases de Java existentes
- Sintaxis mejorada para los tipos existentes y nuevos tipos
- Librería extendida para las clases de Java existentes
- Groovy no soporta la construcción del bucle FOR mediante for(init;test;inc) pero lo hace con un rango

EL LENGUAJE
- Tipos y números
Tipos dinámicos con def
Groovy soporta tipos estáticos por compatibilidad con Java
Todo es un objeto
Autoboxing: int -> Integer e Integer -> int
Duck typing

- Tipos de colecciones
Simplicidad de conceptos
Tres tipos importantes: rangos, listas y mapas
Correspondencia con la semántica de Java salvo en el caso de los rangos
Link de ejemplos -> http://groovy.codehaus.org/JN1015-Collections
Link de ejemplos -> http://groovy.codehaus.org/JN1025-Arrays
Link de ejemplos -> http://groovy.codehaus.org/JN1035-Maps

- String vs GString
 Comillas simples -> String ej. 'Hello world'
 Comillas dobles -> GString ej. "Hello $variable"
 Comillas simples triples -> String 
 ej. ''' cadena de
        caracteres
        multilinea '''
 Comillas dobles triples -> GString
 ej. """ cadena de caracteres
         con $variable
         multilinea """ 

- Estructuras de control
Las estructuras de control se basan Test Booleanos
Las expresiones de los Test Booleanos pueden ser de cualquier tipo no nulo
Reglas para verdadero:
1) Boolean: si el valor es true
2) Matcher: si el matcher tiene una coincidencia
3) Collection: si la colección no está vacía
4) Map: si el mapa no está vacío
5) String, GString: si la cadena no esta vacía
6) Number, Character: si el valor no es cero
7) Ninguno de los anteriores: si la referencia al objeto no es nula

- Closures
Son bloques de código reutilizables
Son objetos sin estado visible desde el exterior, sólo tienen comportamiento
Tienen dos propósitos principales: Ejecución en iteraciones y Manejo de recursos

- GroovyBeans
Son los clásicos JavaBeans
Tienen getters y setters
Es serializable para poder ser persistido
Groovy crea los accesors (getters y setters) y los constructores automáticamente

- Expando
Objeto dinámico: se le pueden añadir métodos y atributos en tiempo de ejecución
Muy parecido a un Map
Orientado al uso en pruebas

- Builders
Es un objeto que implementa el patrón Builder: construir objetos complejos a partir de otros objetos
Su propósito es el de construir estructuras jerárquicas
Se basa en la versatilidad de Groovy
Groovy incorpora BuilderSupport con:
1) DOMBuilder: para construir árboles DOM W3C
2) MarkupBuilder: para construir estrucuturas de marcas como XML
3) AntBuilder: para construir scripts de Apache Ant
4) SwingBuilder, SWTBuilder: para construir GUIs

* Guía para empezar
http://groovy.codehaus.org/Getting+Started+Guide
* Ejemplos
http://groovy.codehaus.org/Cookbook+Examples
* Una clase maravillosa
http://groovy.codehaus.org/ExpandoMetaClass
* Operadores en Groovy
http://groovy.codehaus.org/Operators
* Lo nuevo de Groovy 2.0 (@TypeChecked y @CompileStatic)
Artículo de Guillaume Laforge
Presentación de Guillaume Laforge en SpringOne2GX (2012)
* Adelanto de Groovy 2.1 y su roadmap
Presentación de Guillaume Laforge en Groovy/Grails eXchange conference (2012)

GROOVY TRICKS
- Ordenar Mapa por valor
//sort by map values
def map = [a:3, b:2, c:1]
map = map.sort {it.value}
assert map == [c:1, b:2, a:3]

//sort by map values in reverse
map = [a:1, b:2, c:3]
map = map.sort {a, b -> b.value <=> a.value}
assert map == [c:3, b:2, a:1]

- Ordenar Mapa por clave
//sort by map keys
def map = [a:3, b:2, c:1]
map = map.sort {it.key}
assert map == [a:3, b:2, c:1]

//sort by map keys in reverse
map = [a:1, b:2, c:3]
map = map.sort {a, b -> b.key <=> a.key}
assert map == [c:3,b:2,a:1]

- Ordenar Lista por un campo
List list = [
    [id:0, firstName: 'Sachin', lastName: 'Tendulkar', age: 40 ],
    [id:1, firstName: 'Sachin', lastName: 'Tendulkar', age: 103 ],
    [id:2, firstName: 'Ajay', lastName: 'Tendulkar', age: 48 ],
    [id:3, firstName: 'Virendra', lastName: 'Sehwag', age: 5 ],
    [id:4, firstName: 'Virendra', lastName: 'Sehwag', age: 50 ],
    [id:5, firstName: 'Sachin', lastName: 'Nayyar', age: 15 ]
]

list.sort{it.age}
//In reverse
list.sort{a, b -> b.age <=> a.age}

- Ordenar Lista por más de un campo
List list = [
    [id:0, firstName: 'Sachin', lastName: 'Tendulkar', age: 40 ],
    [id:1, firstName: 'Sachin', lastName: 'Tendulkar', age: 103 ],
    [id:2, firstName: 'Ajay', lastName: 'Tendulkar', age: 48 ],
    [id:3, firstName: 'Virendra', lastName: 'Sehwag', age: 5 ],
    [id:4, firstName: 'Virendra', lastName: 'Sehwag', age: 50 ],
    [id:5, firstName: 'Sachin', lastName: 'Nayyar', age: 15 ]
]

list.sort { a,b ->
   a.firstName <=> b.firstName ?: a.lastName <=> b.lastName ?: a.age <=> b.age
}*.id
* Output ids-> [2, 5, 0, 1, 3, 4]

- Añadiendo o sobreescribiendo métodos
class Book {
   String title
}
Book.metaClass.titleInUpperCase << {-> title.toUpperCase() }
def b = new Book(title:"The Stand")
assert "THE STAND" == b.titleInUpperCase()
* El operador << es usado para añadir un método nuevo. Si hay que sobreescribir un método que ya existe sería con el operador =
Book.metaClass.toString = {-> title.toUpperCase() }

- Métodos take() y drop() de una Lista
def list = ['Simple', 'list', 'with', 5, 'items']

assert list.take(1) == ['Simple']
assert list.take(2) == ['Simple', 'list']
assert list.take(0) == []
// Whole list, because we take more items then the size of list
assert list.take(6) == ['Simple', 'list', 'with', 5, 'items']

assert list.drop(1) == ['list', 'with', 5, 'items']
assert list.drop(3) == [5, 'items']
assert list.drop(5) == []
assert list.drop(0) == ['Simple', 'list', 'with', 5, 'items']
assert list == ['Simple', 'list', 'with', 5, 'items']

// After reading Tim Yates' comment I have added 
// more samples showing drop() and take() also work on
// Maps, Iterators, CharSequences and arrays.
def array = ['Rock on!', 'Groovy baby!'] as String[]
assert array.take(1) == ['Rock on!'] as String[]
assert array.drop(1) == ['Groovy baby!'] as String[]

def range = 0..10
assert range.take(2) == [0,1]
assert range.take(4) == 0..3
assert range.drop(5) == 5..10

def map = [1: 'one', 2: 'two', 3: 'three']
assert map.take(2) == [1: 'one', 2: 'two']
assert map.drop(2) == [3: 'three']
assert map.drop(3) == [:]

def s = 'Hello Groovy world!'
assert s.take(5) == 'Hello'
assert s.drop(6) == 'Groovy world!'

- Extensiones de ficheros para scripts en Groovy
Por defecto las extensiones pueden ser: .groovy .gvy .gy .gsh Se ejecuta en la consola con el comando
$ groovy sample.gsh
$ groovy sample

- Ejecutar un script via URL
En la consola ejecutar el comando
$ groovy http://www.edusama.com/samples/remotesample.groovy

- Ejecutar un script en tiempo de ejecución
Script ya creado Area.groovy
// Area.groovy  
Float r = "${radius}".toFloat()  //radius will be passed as an argument to this script.  
return "Area of the circle : " + Math.PI*r*r;

Para ejecutar Area.groovy
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
  
GroovyScriptEngine gse = new GroovyScriptEngine("/home/divya/Desktop");  
Binding binding = new Binding();  
binding.setVariable("radius", 5); // Argument to be passed is "radius" with value 5  
String areaOfCircle = gse.run("Area.groovy", binding);
println areaOfCircle  
// Output:  Area of the circle : 78.53981633974483

GRAILS TRICKS
- Patrón para capturar excepciones en acciones Grails
def myService
  
def myAction = {
  
  try{
      myService.doSomeTask()
  }catch(BusinessException ex ){
      flash.message = ex.message
  }catch(Exception ex){
      flash.message = "Internal Error"
      log.error("Unknown Exception:", ex);
  }
}

- Lista de objetos accesibles desde los CONTROLLERS
request, response, session, servletContext, grailsApplication, params, flash

- Lista de objetos accesibles desde las GSPs
request, response, session, applicationContext, grailsApplication, params, flash

- Renderizar vistas o templates fuera de los Controladores
import grails.gsp.PageRenderer

class RenderService {

    PageRenderer groovyPageRenderer

    String createConfirmMessage() {
        groovyPageRenderer.render view: '/email/confirm', model: [username: findUsername()]
    }

    String createWelcomeMessage() {
        groovyPageRenderer.render template: '/email/welcome', model: [username: findUsername()]
    }

    private String findUsername() {
        // Lookup username, for this example we return a
        // simple String value.
        'edu'
    }
}
- Generando links fuera de los Controladores y las TagLibs
import org.codehaus.groovy.grails.web.mapping.LinkGenerator

class LinkService {

    // Inject link generator
    LinkGenerator grailsLinkGenerator

    String generate() {
        // Generate: http://localhost:8080/link-generator/sample/show/100
        grailsLinkGenerator.link(controller: 'sample', action: 'show', id: 100, absolute: true)
    }

    String resource() {
        // Generate: /link-generator/css/main.css
        grailsLinkGenerator.resource(dir: 'css', file: 'main.css')
    }

    String contextPath() {
        // Generate: /link-generator
        grailsLinkGenerator.contextPath
    }

    String serverUrl() {
        // Generate: http://localhost:8080/link-generator
        grailsLinkGenerator.serverBaseURL
    }
}

- Conseguir la session en una Servicio
import org.springframework.web.context.request.RequestContextHolder

def user = RequestContextHolder.currentRequestAttributes().getSession()

- Dependencias circulares de servicios de Grails
A veces cuando tenemos muchos servicios en una aplicación y estamos continuamente inyectándolos unos con otros, se puede producir este error:
 Error creating bean with name '(inner bean)': Initialization of bean failed;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'queueService':
org.springframework.beans.factory.FactoryBeanNotInitializedException:
FactoryBean is not fully initialized yet
        at java.security.AccessController.doPrivileged(Native Method)
        at RunApp_groovy$_run_closure2_closure7.doCall(RunApp_groovy:69)
Para solucionarlo se debería de inyectar los servicios de la siguiente manera:
import org.springframework.context.*

class QueueService implements ApplicationContextAware {

    ApplicationContext applicationContext

    def feedServiceBean  // note the change of name to stop injection

    def slurpOffQueue() {

        feedServiceBean = applicationContext.getBean("feedService")
        feedServiceBean.doStuff()
    }
}

- Evento onload en etiqueta body
En Grails, si pones un evento onload en una GSP cualquiera, no se lanzará el evento.
<body onload="someJSFunction()">
  ...
</body>

Y esto es se debe a que Grails sólo coge el body del layout y no el de la GSP. Para poder lanzar este tipo de eventos se tendrá que modificar el body del layout de Sitemesh de la siguiente manera,
<body onload="${pageProperty(name:'body.onload')}">
  ...
</body>

No hay comentarios:

Publicar un comentario