stringtranslate.com

Apache Groovy

Apache Groovy es un lenguaje de programación orientado a objetos compatible con la sintaxis de Java para la plataforma Java . Es un lenguaje estático y dinámico con características similares a las de Python , Ruby y Smalltalk . Se puede utilizar como lenguaje de programación y como lenguaje de script para la plataforma Java, se compila en código de bytes de máquina virtual Java (JVM) e interopera sin problemas con otros códigos y bibliotecas de Java . Groovy utiliza una sintaxis de llaves similar a la de Java. Groovy admite cierres , cadenas de varias líneas y expresiones incrustadas en cadenas . Gran parte del poder de Groovy reside en sus transformaciones AST , activadas a través de anotaciones.

Groovy 1.0 se lanzó el 2 de enero de 2007 y Groovy 2.0 en julio de 2012. Desde la versión 2, Groovy se puede compilar estáticamente , ofreciendo inferencia de tipos y rendimiento cercano al de Java. [4] [5] Groovy 2.4 fue el último lanzamiento importante bajo el patrocinio de Pivotal Software que finalizó en marzo de 2015. [6] Desde entonces, Groovy ha cambiado su estructura de gobernanza a un Comité de Gestión de Proyectos en la Apache Software Foundation . [7]

Historia

James Strachan habló por primera vez sobre el desarrollo de Groovy en su blog en agosto de 2003. [8] En marzo de 2004, Groovy fue enviado al JCP como JSR 241 [9] y aceptado por votación. Se publicaron varias versiones entre 2004 y 2006. Después de que comenzara el esfuerzo de estandarización del Java Community Process (JCP), la numeración de las versiones cambió y se publicó una versión llamada "1.0" el 2 de enero de 2007. Después de varias versiones beta y candidatas a lanzamiento numeradas 1.1, el 7 de diciembre de 2007 se publicó Groovy 1.1 Final y se renumeró inmediatamente como Groovy 1.5 para reflejar los muchos cambios realizados.

En 2007, Groovy ganó el primer premio en el premio a la innovación JAX 2007. [10] En 2008, Grails , un marco web Groovy , ganó el segundo premio en el premio a la innovación JAX 2008. [11]

En noviembre de 2008, SpringSource adquirió la empresa Groovy and Grails (G2One). [12] En agosto de 2009, VMware adquirió SpringSource. [13]

En abril de 2012, después de ocho años de inactividad, el jefe de especificaciones cambió el estado del JSR 241 a inactivo. [9]

Strachan había abandonado el proyecto en silencio un año antes del lanzamiento de Groovy 1.0 en 2007. [ cita requerida ] En octubre de 2016, Strachan declaró: "Todavía me encanta Groovy (¡las canalizaciones de Jenkins son tan maravillosas!), Java, Go, Typescript y Kotlin". [14]

El 2 de julio de 2012 se lanzó Groovy 2.0, que, entre otras novedades, añadió compilación estática y comprobación de tipos estática .

Cuando la empresa conjunta Pivotal Software fue escindida por EMC Corporation (EMC) y VMware en abril de 2013, Groovy y Grails pasaron a formar parte de su cartera de productos. Pivotal dejó de patrocinar a Groovy y Grails a partir de abril de 2015. [6] Ese mismo mes, Groovy cambió su estructura de gobierno de un repositorio de Codehaus a un Comité de Gestión de Proyectos (PMC) en la Apache Software Foundation a través de su incubadora. [7] Groovy se graduó de la incubadora de Apache y se convirtió en un proyecto de alto nivel en noviembre de 2015. [15]

El 7 de febrero de 2020 se lanzó Groovy 3.0. [16] La versión 4.0 se lanzó el 25 de enero de 2022. [17]

Características

La mayoría de los archivos Java válidos también son archivos Groovy válidos. Aunque los dos lenguajes son similares, el código Groovy puede ser más compacto, porque no necesita todos los elementos que necesita Java. [18] Esto hace posible que los programadores Java aprendan Groovy gradualmente comenzando con la sintaxis Java familiar antes de adquirir más modismos de programación Groovy . [19]

Las características de Groovy que no están disponibles en Java incluyen tipado estático y dinámico (con la palabra clave def), sobrecarga de operadores , sintaxis nativa para listas y matrices asociativas (mapas), soporte nativo para expresiones regulares , iteración polimórfica, interpolación de cadenas , métodos auxiliares agregados y el operador de navegación segura ?. para verificar automáticamente punteros nulos (por ejemplo, variable?.method(), o variable?.field). [20]

Desde la versión 2, Groovy también admite modularidad (poder enviar solo los jar necesarios según las necesidades del proyecto, reduciendo así el tamaño de la biblioteca de Groovy), verificación de tipos, compilación estática, mejoras de sintaxis de Project Coin, bloques multicatch y mejoras de rendimiento continuas utilizando la invokedynamicinstrucción introducida en Java 7. [ 21]

Groovy ofrece compatibilidad nativa con varios lenguajes de marcado, como XML y HTML , mediante una sintaxis de modelo de objetos de documento (DOM) en línea. Esta característica permite la definición y manipulación de muchos tipos de activos de datos heterogéneos con una sintaxis y una metodología de programación uniformes y concisas. [ cita requerida ]

A diferencia de Java, un archivo de código fuente de Groovy se puede ejecutar como un script (sin compilar) , si contiene código fuera de cualquier definición de clase, si es una clase con un método principal o si es un Runnable o GroovyTestCase . Un script de Groovy se analiza, compila y genera por completo antes de ejecutarse (de manera similar a Python y Ruby). Esto ocurre en segundo plano y la versión compilada no se guarda como un artefacto del proceso. [22]

GroovyBeans, propiedades

Los GroovyBeans son la versión de Groovy de los JavaBeans . Groovy genera métodos get y setter de forma implícita. En el código siguiente, setColor(String color)y getColor()se generan de forma implícita. Las dos últimas líneas, que parecen acceder directamente al color, en realidad están llamando a los métodos generados de forma implícita. [23]

clase AGroovyBean { Cadena color }    def myGroovyBean = nuevo AGroovyBean ()    myGroovyBean . setColor ( 'bebé azul' ) assert myGroovyBean . getColor () == 'bebé azul'   myGroovyBean . color = 'peltre' assert myGroovyBean . color == 'peltre'     

Groovy ofrece una sintaxis simple y consistente para manejar listas y mapas , que recuerda a la sintaxis de matriz de Java . [24]

def movieList = [ 'Dersu Uzala' , 'Ran' , 'Seven Samurai' ] // Parece una matriz, pero es una lista assert movieList [ 2 ] == 'Seven Samurai' movieList [ 3 ] = 'Casablanca' // Agrega un elemento a la lista assert movieList . size () == 4               def monthMap = [ 'January' : 31 , 'February' : 28 , 'March' : 31 ] // Declara un mapa assert monthMap [ 'March' ] == 31 // Accede a una entrada monthMap [ 'April' ] = 30 // Agrega una entrada al mapa assert monthMap . size () == 4                        

Extensión del prototipo

Groovy ofrece soporte para la extensión de prototipos a través de , módulos de extensión (solo en Groovy 2), categoríasExpandoMetaClass similares a Objective-C y . [25]DelegatingMetaClass

ExpandoMetaClassofrece un lenguaje específico de dominio (DSL) para expresar los cambios en la clase fácilmente, similar al concepto de clase abierta de Ruby :

Número . metaClass { sqrt = { Math . sqrt ( delegado ) } }      afirmar 9 . sqrt () == 3 afirmar 4 . raíz cuadrada () == 2      

Los cambios en el código de Groovy a través de la creación de prototipos no son visibles en Java, ya que cada invocación de atributo o método en Groovy pasa por el registro de metaclases. Solo se puede acceder al código modificado desde Java yendo al registro de metaclases.

Groovy también permite anular métodos como getProperty(), propertyMissing()entre otros, lo que permite al desarrollador interceptar llamadas a un objeto y especificar una acción para ellas, de una manera simplificada y orientada a aspectos . El siguiente código permite que la clase java.lang.Stringresponda a la hexpropiedad:

enumeración Color { NEGRO ( '#000000' ), BLANCO ( '#FFFFFF' ), ROJO ( '#FF0000' ), AZUL ( '#0000FF' ) Cadena hexadecimal Color ( Cadena hexadecimal ) { this . hex = hex } }                 Cadena . metaClass . getProperty = { Cadena propiedad -> def stringColor = delegate if ( propiedad == 'hex' ) { Color . values ​​(). find { it . name (). equalsIgnoreCase stringColor }?. hex } }                    afirmar "BLANCO" . hex == "#FFFFFF" afirmar "AZUL" . hex == "#0000FF" afirmar "NEGRO" . hex == "#000000" afirmar "VERDE" . hex == null            

El marco Grails utiliza ampliamente la metaprogramación para permitir buscadores dinámicos GORMUser.findByName('Josh') , como y otros. [26]

Punto y paréntesis

La sintaxis de Groovy permite omitir los paréntesis y los puntos en algunas situaciones. El siguiente código de Groovy

tomar ( café ) con ( azúcar , leche ) y ( licor ) 

se puede escribir como

tomar café con azúcar , leche y licor      

permitiendo el desarrollo de lenguajes específicos de dominio (DSL) que parecen inglés simple.

Programación funcional

Aunque Groovy es principalmente un lenguaje orientado a objetos, también ofrece características de programación funcional .

Cierres

Según la documentación de Groovy: "Los cierres en Groovy funcionan de manera similar a un 'puntero de método', lo que permite escribir y ejecutar código en un punto posterior en el tiempo". [27] Los cierres de Groovy admiten variables libres, es decir, variables que no se le han pasado explícitamente como parámetro, pero que existen en su contexto de declaración, aplicación parcial (que denomina ' currying ' [28] ), delegación, parámetros implícitos, tipados y no tipados.

Cuando se trabaja con colecciones de un tipo determinado, se puede inferir el cierre pasado a una operación sobre la colección:

lista = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]          /* * Los números distintos de cero se convierten en verdaderos, por lo que cuando it % 2 == 0 (par), es falso. * El IDE puede inferir el tipo del parámetro "it" implícito como un entero. * También se puede escribir como: * list.findAll { Integer i -> i % 2 } * list.findAll { i -> i % 2 } */ def odds = list . findAll { it % 2 }        afirmar probabilidades == [ 1 , 3 , 5 , 7 , 9 ]       

Se puede escribir un grupo de expresiones en un bloque de cierre sin referencia a una implementación y el objeto que responde se puede asignar en un punto posterior mediante delegación:

// Este bloque de código contiene expresiones sin referencia a una implementación def operaciones = { declare 5 sum 4 divide 3 print }          
/* * Esta clase manejará las operaciones que se pueden usar en el cierre anterior. Se podría declarar otra clase * que tenga los mismos métodos, pero que use, por ejemplo, operaciones de servicio web * en los cálculos. */ class Expression { BigDecimal value     /*  * Aunque se pasa un entero como parámetro, se convierte en un BigDecimal, como se  definió. Si la clase tuviera un método 'declare(Integer value)', se utilizaría en su lugar.  */ def declare ( BigDecimal value ) { this . value = value } def sum ( BigDecimal valueToAdd ) { this . value += valueToAdd } def divide ( BigDecimal divisor ) { this . value /= divisor } def propertyMissing ( String property ) { if ( property == "print" ) println value } }                                      
//Aquí se define quién va a responder las expresiones en el bloque de código anterior. operaciones . delegado = new Expresión () operaciones ()   

Curry

Esta característica de Groovy, que suele denominarse aplicación parcial , [28] permite que los parámetros de los cierres se establezcan en un parámetro predeterminado en cualquiera de sus argumentos, creando un nuevo cierre con el valor límite. Si se proporciona un argumento al curry()método, se solucionará el argumento uno. Si se proporcionan N argumentos, se solucionarán los argumentos 1...N.

def joinTwoWordsWithSymbol = { símbolo , primero , segundo -> primero + símbolo + segundo } assert joinTwoWordsWithSymbol ( '#' , 'Hola' , 'Mundo' ) == 'Hola#Mundo'                  def concatWords = joinTwoWordsWithSymbol . curry ( ' ' ) assert concatWords ( 'Hola' , 'Mundo' ) == 'Hola Mundo'       def prependHello = concatWords . curry ( 'Hola' ) //def prependHello = joinTwoWordsWithSymbol. curry(' ', 'Hola') assert prependHello ( 'Mundo' ) == 'Hola Mundo'      

Curry también se puede utilizar en la dirección inversa (arreglando los últimos N argumentos) usando rcurry().

def potencia = { valor BigDecimal , potencia BigDecimal -> valor ** potencia }         def cuadrado = potencia . rcurry ( 2 ) def cubo = potencia . rcurry ( 3 )      afirmar potencia ( 2 , 2 ) == 4 afirmar cuadrado ( 4 ) == 16 afirmar cubo ( 3 ) == 27          

Groovy también admite evaluación perezosa , [29] [30] reducción/plegado , [31] estructuras infinitas e inmutabilidad , [32] entre otras. [33]

Procesamiento de JSON y XML

En el procesamiento de XML y la notación de objetos de JavaScript ( JSON ), Groovy emplea el patrón Builder , lo que hace que la producción de la estructura de datos sea menos detallada. Por ejemplo, el siguiente XML:

<languages> <language year= "1995" > <name> Java </name> <paradigm> orientado a objetos </paradigm> <typing> estático </typing> </language> < language year = "1995" > <name> Ruby </name> <paradigm> funcional, orientado a objetos </paradigm> <typing> tipado de pato , dinámico </typing> </language> <language year= "2003" > <name> Groovy </name> <paradigm> funcional, orientado a objetos </paradigm> <typing> tipado de pato , dinámico, estático </typing> </language> </languages>                            

se puede generar mediante el siguiente código Groovy:

def writer = new StringWriter () def builder = new groovy.xml.MarkupBuilder ( writer ) builder.languages ​​{ language ( year: 1995 ) { name "Java" paradigma "orientado a objetos" tipificación " estática " } language ( year : 1995 ) { name " Ruby" paradigma " funcional, orientado a objetos" tipificación "tipificación dinámica" } language ( year: 2003 ) { name "Groovy" paradigma "funcional, orientado a objetos" tipificación " tipificación dinámica, estática" } }                                         

y también se puede procesar de forma continua a través de StreamingMarkupBuilder. Para cambiar la implementación a JSON, se MarkupBuilderpuede cambiar a JsonBuilder. [34]

Para analizarlo y buscar un lenguaje funcional, findAllel método de Groovy puede servir:

def idiomas = nuevo XmlSlurper () . parseText escritor.toString ( )     // Aquí se emplea la sintaxis de expresiones regulares de Groovy para un comparador (=~) que se convertirá a un // valor booleano: verdadero, si el valor contiene nuestra cadena, o falso en caso contrario. def functional = languages ​​. language . findAll { it . paradigm =~ "functional" } assert functional . collect { it . name } == [ "Groovy" , "Ruby" ]               

Interpolación de cadenas

En Groovy, las cadenas se pueden interpolar con variables y expresiones mediante GStrings: [35]

Cuenta BigDecimal = 10.0 def text = "La cuenta muestra actualmente un saldo de $cuenta" assert text == "La cuenta muestra actualmente un saldo de 10.0"         

Las GStrings que contienen variables y expresiones deben declararse mediante comillas dobles.

Una expresión compleja debe estar entre llaves. Esto evita que partes de ella se interpreten como pertenecientes a la cadena circundante en lugar de a la expresión:

BigDecimal minus = 4.0 text = "La cuenta muestra actualmente un saldo de ${account - minus}" assert text == "La cuenta muestra actualmente un saldo de 6.0"        // Sin los corchetes para aislar la expresión, esto daría como resultado: text = "La cuenta muestra actualmente un saldo de $cuenta - menos" assert text == "La cuenta muestra actualmente un saldo de 10.0 - menos"     

La evaluación de expresiones se puede diferir empleando la sintaxis de flecha:

BigDecimal tax = 0.15 text = "La cuenta muestra actualmente un saldo de ${->account - account*tax}" tax = 0.10       // El valor del impuesto se modificó DESPUÉS de la declaración de GString. Las variables de expresión se vinculan solo cuando la expresión debe evaluarse realmente: assert text == "La cuenta muestra actualmente un saldo de 9.000"   

Transformación de árbol de sintaxis abstracta

Según la propia documentación de Groovy, "Cuando el compilador de Groovy compila scripts y clases de Groovy, en algún punto del proceso, el código fuente terminará siendo representado en la memoria en forma de un árbol de sintaxis concreta, y luego se transformará en un árbol de sintaxis abstracta. El propósito de las transformaciones AST es permitir que los desarrolladores se conecten al proceso de compilación para poder modificar el AST antes de que se convierta en código de bytes que será ejecutado por la JVM. Las transformaciones AST proporcionan a Groovy capacidades mejoradas de metaprogramación en tiempo de compilación que permiten una gran flexibilidad a nivel de lenguaje, sin una penalización del rendimiento en tiempo de ejecución". [36]

Ejemplos de AST en Groovy son:

Entre otros.

El marco de pruebas Spock utiliza transformaciones AST para permitir al programador escribir pruebas en una sintaxis no compatible con Groovy, pero el código relevante se manipula luego en el AST para convertirlo en código válido. [37] Un ejemplo de dicha prueba es:

def "el máximo de #a y #b es #c" () { esperar: Math . max ( a , b ) == c          donde : a | b || c 3 || 5 || 5 7 || 0 || 7 0 || 0 }                    

Rasgos

Según la documentación de Groovy, " Los rasgos son una construcción estructural del lenguaje que permite: la composición de comportamientos, la implementación de interfaces en tiempo de ejecución, la anulación de comportamientos y la compatibilidad con la compilación/compilación de tipos estáticos".

Los rasgos pueden considerarse interfaces que contienen tanto implementaciones predeterminadas como estados. Un rasgo se define utilizando la palabra clave rasgo:

rasgo FlyingAbility { /* declaración de un rasgo */ String fly () { "¡Estoy volando!" } /* declaración de un método dentro de un rasgo */ }         

Luego, se puede utilizar como una interfaz normal usando la palabra clave implements:

clase Bird implementa FlyingAbility {} /* Agrega el rasgo FlyingAbility a las capacidades de la clase Bird */ def bird = new Bird () /* instancia un nuevo Bird */ assert bird . fly () == "¡Estoy volando!" /* la clase Bird obtiene automáticamente el comportamiento del rasgo FlyingAbility */              

Los rasgos permiten una amplia gama de habilidades, desde la composición simple hasta la prueba.

Adopción

Algunos ejemplos notables de adopción de Groovy incluyen:

Soporte IDE

Muchos entornos de desarrollo integrados (IDE) y editores de texto admiten Groovy:

Dialectos

Hay una implementación alternativa de Groovy:

Véase también

Referencias

Citas

  1. ^ "Versión 4.0.23". 9 de septiembre de 2024. Consultado el 21 de septiembre de 2024 .
  2. ^ "Lanzamientos - apache/groovy" . Consultado el 9 de abril de 2020 a través de GitHub .
  3. ^ "Groovy Goodness: extensiones de archivo de script Groovy predeterminadas".
  4. ^ "Rendimiento de Groovy 2.0 comparado con Java". 25 de agosto de 2012.
  5. ^ "Prueba de rendimiento simple de Java vs Groovy2.0 vs Scala". 10 de julio de 2012. Archivado desde el original el 10 de diciembre de 2012. Consultado el 7 de octubre de 2012 .
  6. ^ ab "Groovy 2.4 y Grails 3.0 serán los últimos lanzamientos importantes bajo el patrocinio de Pivotal". 19 de enero de 2015.
  7. ^ ab "Groovy se une a la incubadora Apache". 11 de marzo de 2015.
  8. ^ James Strachan (29 de agosto de 2003). «Groovy: el nacimiento de un nuevo lenguaje dinámico para la plataforma Java». Archivado desde el original el 1 de septiembre de 2003.
  9. ^ ab "Proceso de la comunidad Java JSR 241".
  10. ^ "Groovy gana el primer premio en los premios a la innovación JAX 2007". 26 de abril de 2007. Archivado desde el original el 13 de mayo de 2015. Consultado el 7 de octubre de 2012 .
  11. ^ "Dicen que pueden pasar muchas cosas con una taza de café". Archivado desde el original el 19 de abril de 2011. Consultado el 7 de octubre de 2012 .
  12. ^ "SpringSource adquiere la empresa Groovy y Grails (G2One)". 11 de noviembre de 2008.
  13. ^ "VMWare adquiere SpringSource". 10 de agosto de 2009.
  14. ^ "Tweet de James Strachan". 24 de noviembre de 2016. Consultado el 24 de noviembre de 2016 .
  15. ^ "Anuncio en la lista de correo de desarrollo".
  16. ^ "Lanzamiento de GROOVY_3_0_0 · apache/groovy". GitHub . Consultado el 27 de marzo de 2024 .
  17. ^ "Lanzamiento de GROOVY_4_0_0 · apache/groovy". GitHub . Consultado el 27 de marzo de 2024 .
  18. ^ König 2007, pág. 32
  19. ^ "Directrices de estilo y características del lenguaje Groovy para desarrolladores de Java". Groovy.codehaus.org. Archivado desde el original el 17 de enero de 2015. Consultado el 22 de enero de 2015 .
  20. ^ "Groovy – Diferencias con Java". Groovy.codehaus.org. Archivado desde el original el 2009-03-17 . Consultado el 2013-08-12 .
  21. ^ "¿Qué novedades hay en Groovy 2.0?". 28 de junio de 2012.
  22. ^ König 2007, págs. 37-8
  23. ^ König 2007, págs. 38-9
  24. ^ König 2007, págs. 41-3
  25. ^ "JN3525-MetaClasses". Archivado desde el original el 1 de octubre de 2012. Consultado el 7 de octubre de 2012 .
  26. ^ "Técnicas de metaprogramación en Groovy y Grails". 11 de junio de 2009.
  27. ^ "Groovy - Cierres". Archivado desde el original el 22 de mayo de 2012.
  28. ^ ab "¿Groovy llama a una aplicación parcial 'currying'?", 10 de agosto de 2013
  29. ^ "Groovy - Lazy Transformation". Archivado desde el original el 8 de octubre de 2012. Consultado el 7 de octubre de 2012 .
  30. ^ "Notas al margen: listas perezosas en Groovy". 3 de febrero de 2011.
  31. ^ "Groovy's Fold". 20 de junio de 2011. Archivado desde el original el 13 de febrero de 2015. Consultado el 12 de febrero de 2015 .
  32. ^ "Programación funcional con Groovy". 5 de noviembre de 2011.
  33. ^ "Programación de funciones en Groovy". Archivado desde el original el 8 de octubre de 2012. Consultado el 7 de octubre de 2012 .
  34. ^ "JsonBuilder". Archivado desde el original el 2 de octubre de 2012. Consultado el 7 de octubre de 2012 .
  35. ^ "Cuerdas Groovy - Diferentes maneras de crearlas". 26 de diciembre de 2009.
  36. ^ "Metaprogramación en tiempo de compilación: transformaciones AST". Archivado desde el original el 14 de octubre de 2012. Consultado el 7 de octubre de 2012 .
  37. ^ King, Paul (2020). "Una historia del lenguaje de programación Groovy". Proc. ACM Program. Lang . 4 : 53. doi : 10.1145/3386326 .
  38. ^ "Documentación de ScriptRunner".
  39. ^ "Comunicado de prensa de ScriptRunner con estadísticas de adopción".
  40. ^ "Groovy DSL para la lógica empresarial de OFBiz". Wiki abierta del proyecto Apache OFBiz .
  41. ^ "Ejemplos de métodos simples con Groovy". Wiki abierta del proyecto Apache OFBiz .
  42. ^ "Grails en LinkedIn" . Consultado el 2 de junio de 2015 .
  43. ^ "Scripting Groovy integrado". www.logicmonitor.com . Consultado el 20 de noviembre de 2020 .
  44. ^ "Oleoducto Jenkins".
  45. ^ Rocher, Graeme (2 de octubre de 2008). "Graeme Rocher's Blog: Sky.com se relanza escrito en Grails". Graeme Rocher's Blog . Consultado el 2 de junio de 2015 .
  46. ^ Análisis de seguridad de las aplicaciones emergentes para hogares inteligentes
  47. ^ "Scripting y la biblioteca de scripts | Scripting y propiedades". www.soapui.org . Consultado el 2 de junio de 2015 .
  48. ^ "Capítulo 11. Integración de Groovy". docs.jboss.org . Consultado el 2 de junio de 2015 .
  49. ^ "vCalc, la primera plataforma social para el mundo de las matemáticas". 4 de noviembre de 2014. Consultado el 5 de mayo de 2016 .
  50. ^ "Wired.Com" (PDF) . www.springsource.org . Consultado el 2 de junio de 2015 .
  51. ^ "XWiki SAS" (PDF) . www.springsource.org . Consultado el 2 de junio de 2015 .
  52. ^ "Documentación de Grooscript". 12 de septiembre de 2016. Archivado desde el original el 28 de junio de 2017. Consultado el 4 de julio de 2017 .
  53. ^ "Presentación en SpringOne/2GX sobre Grooscript". 13 de diciembre de 2015.
  54. ^ "Conversiones en línea de Grooscript". 15 de mayo de 2017. Archivado desde el original el 9 de julio de 2017 . Consultado el 4 de julio de 2017 .

Fuentes

Enlaces externos