LLamando scripts Groovy desde código Groovy

La clase GroovyShell provee las funcionalidades necesarias para ejecutar scripts controlando su entorno de ejecución.

Llamada simple

En este ejemplo, el script Script2.groovy

generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ cat -n Script2.groovy
     1  println "In Script2"
     2  shell = new GroovyShell()
     3  shell.evaluate(new  File('Script1.groovy'))
llama al script Script1.groovy:
generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ cat -n Script1.groovy
     1  println "Hello from Script1"
Para ello primero se crea un objeto GroovyShell y después se llama al método evaluate: En el ejemplo evaluate recibe una cadena conteniendo el nombre de fichero, pero es posible obtener el script de diversas formas:

Sigue el resultado de una ejecución:
generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ groovy Script2.groovy
In Script2
Hello from Script1
También es posible llamar simplemente a evaluate:
$ cat -n Script3.groovy
     1  println "In Script3"
     2  evaluate(new  File('Script1.groovy'))
$ groovy Script3.groovy
In Script3
Hello from Script1

LLamada con parámetros y retorno de resultados

Este script hace uso de una variable $name que definiremos en otro script:

generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ cat -n Script1a.groovy
     1  println "Hello $name"
     2  name = "Dan"

Aunque los atributos y variables locales deben ser declarados antes de su uso dentro de una clase, en los scripts es posible usar variables no declaradas. En ese caso se asume que las variables vienen del binding del script y que serán añadidas a este si aún no están allí.

In computer science, binding is the creation of a simple reference to something that is larger and more complicated and used frequently. The simple reference can be used instead of having to repeat the larger thing. A binding is such a reference

The terms language binding and name binding refer to both the linking of libraries to application programs, and to the way symbols (variable names) are handled by compilers.

In programming languages, these terms mean the references of an identifier to a value. In this context, binding is the act of associating a name or symbol with a machine address, and this association may occur either at compile time, in which case it is called 'static linking', or it may occur dynamically at runtime, which is called 'dynamic linking'. The terms binding and linking are used interchangeably in some contexts.

Static binding has an additional characteristic: it prevents libraries from being updated (recompiled) independently of applications.

El siguiente script crea un objeto GroovyShell pasándole como argumento el objeto Binding del script actual. Este objeto representa los binding (el mapa) de las variables en el script. Al pasarlo como argumento conseguimos que las variables pueden ser alteradas desde fuera del script y creadas fuera y pasadas al mismo. Citando la documentación:

The public class Binding represents the variable bindings of a script which can be altered from outside the script object or created outside of a script and passed into it.

generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ cat -n Script2a.groovy
     1  println "In Script2a"
     2  name = "Juana"
     3
     4  shell = new GroovyShell(binding)
     5  result = shell.evaluate(new File('Script1a.groovy'))
     6
     7  println "Script1a returned: $result"
     8  println "Hello $name"

En efecto:

generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ groovy Script2a.groovy
In Script2a
Hello Juana
Script1a returned: Dan
Hello Dan

El valor retornado por evaluate es el valor retornado por el script.

Protegiendo el Entorno del LLamador

En el ejemplo anterior pasamos el binding del llamador al script llamado. Crearemos un nuevo binding si queremos aislar la ejecución del script llamado:

generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ cat Script5.groovy
Binding binding = new Binding();
binding.setVariable("foo", new Integer(2));
GroovyShell shell = new GroovyShell(binding);

Object value = shell.evaluate(new File('Script1b.groovy'))
println value
println binding.getVariable("x")

assert value.equals(new Integer(20));
assert binding.getVariable("x").equals(new Integer(123));

Estos son los contenidos del script Script1b.groovy:

generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ cat Script1b.groovy
println 'Hello World!'
x = 123
return foo * 10
Al ejecutar Script5.groovy vemos como la variable foo está en el binding de Script1b.groovy cuando éste se ejecuta:
generaciondecodigos@nereida:~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ groovy Script5.groovy
Hello World!
20
123
Además Script5.groovy obtiene e imprime el valor retornado por Script1b.groovy y accede a la variable x del mismo.

El Método run

Utilizaremos el método run en vez de evaluate si se quieren pasar los argumentos usando la línea de comandos:

El siguiente ejemplo ilustra el modo de uso. Código del llamador:

~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ cat -n run.groovy
     1  binding = new Binding()
     2  binding.setVariable("name", "Juan")
     3
     4  shell = new GroovyShell(binding)
     5
     6  value = shell.run(new File('Script1c.groovy'), ['a', 'b'])
     7
     8  println "Script1c returned ${value}"
     9
    10  println "z = ${binding.getVariable("z")}"
Este es el código del script llamado:
~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ cat -n Script1c.groovy
     1  println "Script1c: args received ${args}"
     2  println "Script1c: Hello $name"
     3  name = "Dan"
     4  z = 4
     5  8
y este el resultado de la ejecución:
~/src/groovy/separatedcomp/groovyscriptsfromgroovy$ groovy run.groovy
Script1c: args received [a, b]
Script1c: Hello Juan
Script1c returned 8
z = 4

El método parse

El método parse de los objetos GroovyShell retorna un objeto de la clase Script. Estos objetos disponen de un método run:

$ cat parseandrun.groovy
Binding binding = new Binding()
binding.setVariable("x", new Integer(2))
def shell = new GroovyShell(binding)

def script = shell.parse(
'''
  x++
  println x
'''
)

10.times {
  script.run()
}
Cuando se ejecuta el programa anterior produce el siguiente resultado:
$ groovy parseandrun.groovy
3
4
5
6
7
8
9
10
11
12

Ejemplo: Una Calculadora

El siguiente programa pone en práctica lo aprendido para proveer una calculadora. Las cadenas leídas en stdin son evaluadas dentro de un script Groovy y el resltado es mostrado por pantalla:

~/Lgroovy/separatedcomp/groovyscriptsfromgroovy$ cat -n calc.groovy 
     1  def stdin = new BufferedReader(new InputStreamReader(System.in))
     2  Binding binding = new Binding()
     3  def shell = new GroovyShell(binding)
     4  
     5  int c = 1;
     6  print "> "
     7  stdin.eachLine{ input ->
     8    def script = shell.parse(input)
     9    def a = script.run()
    10    println a
    11    print "> "
    12  }

sigue un ejemplo de ejecución:

generaciondecodigos@nereida:~/Lgroovy/separatedcomp/groovyscriptsfromgroovy$ groovy calc.groovy 
> a =2*3
6
> a+2
8
> b = a-3
3
> Math.sqrt(b)
1.7320508075688772
> ^D
Hemos pulsado la combinación CTRL-D para producir la señal de final de entrada.

Véase



Subsecciones
Casiano Rodríguez León
2010-04-30