Incluyendo un guión maravilloso en otro maravilloso


97

He leído cómo simplemente importar un archivo maravilloso en otro script maravilloso

Quiero definir funciones comunes en un archivo maravilloso y llamar a esas funciones desde otros archivos maravillosos.

Entiendo que esto sería usar Groovy como un lenguaje de secuencias de comandos, es decir, no necesito clases / objetos. Estoy intentando algo como dsl que se puede hacer en groovy. Todas las variables se afirmarán desde Java y quiero ejecutar un script maravilloso en un shell.

¿Es esto posible en absoluto? ¿Alguien puede dar algún ejemplo?


2

Respuestas:


107
evaluate(new File("../tools/Tools.groovy"))

Pon eso en la parte superior de tu guión. Eso traerá el contenido de un archivo maravilloso (simplemente reemplace el nombre del archivo entre las comillas dobles con su script maravilloso).

Hago esto con una clase sorprendentemente llamada "Tools.groovy".


7
El nombre del archivo debe ajustarse a las reglas de denominación de clases de Java para que esto funcione.
willkil

2
Pregunta: ¿Cómo puedo pasar argumentos al script que estoy evaluando usando esta sintaxis?
Steve

3
@steve No puede, pero puede definir una función en ese script que llama con argumentos
Nilzor

11
No funciona ... el script está bien evaluado pero no existe una declaración en el alcance de la persona que llama (def, class, etc.)
LoganMzz

3
Debe devolver un objeto de la llamada uno, luego asignar el resultado de evaluación a una variable.
LoganMzz

45

A partir de Groovy 2.2, es posible declarar una clase de script base con la nueva @BaseScriptanotación de transformación AST.

Ejemplo:

archivo MainScript.groovy :

abstract class MainScript extends Script {
    def meaningOfLife = 42
}

archivo test.groovy :

import groovy.transform.BaseScript
@BaseScript MainScript mainScript

println "$meaningOfLife" //works as expected

1
Sigo recibiendo "incapaz de resolver la clase" cuando uso este método. ¿Qué me recomendarías que hiciera? ¿Hay alguna forma de importar clases personalizadas en otro script maravilloso?
droidnoob

38

Otra forma de hacer esto es definir las funciones en una clase maravillosa y analizar y agregar el archivo a la ruta de clases en tiempo de ejecución:

File sourceFile = new File("path_to_file.groovy");
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(sourceFile);
GroovyObject myObject = (GroovyObject) groovyClass.newInstance();

2
Esta solución realmente funcionó mejor para mí. Cuando intenté usar la respuesta aceptada, recibí un error que decía que mi guión principal maravilloso no podía resolver la clase definida en el guión evaluado. Por lo que vale ...
cBlaine

1
Probé varios enfoques diferentes que se publicaron en SO y solo esto funcionó. Los demás arrojaron errores sobre no poder resolver la clase o los métodos. Esta es la versión que estoy usando Versión Groovy: 2.2.2 JVM: 1.8.0 Proveedor: Oracle Corporation OS: Windows 7.
Kuberchaun

Esto funcionó muy bien. Asegúrese de usarlo GroovyObjectexplícitamente, no es un marcador de posición para su propio nombre de clase.
comprobado el

1
Todavía obtengo: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

Salvavidas. ¡¡Gracias compañero!!
Anjana Silva

30

Creo que la mejor opción es organizar las cosas de utilidad en forma de clases maravillosas, agregarlas a classpath y dejar que el script principal se refiera a ellas mediante la palabra clave de importación.

Ejemplo:

scripts / DbUtils.groovy

class DbUtils{
    def save(something){...}
}

scripts / script1.groovy:

import DbUtils
def dbUtils = new DbUtils()
def something = 'foobar'
dbUtils.save(something)

secuencia de comandos en ejecución:

cd scripts
groovy -cp . script1.groovy

Me pregunto cómo funcionaría esto si tiene una estructura de directorio como con liby srcdirectorios
Gi0rgi0s

9

La forma en que hago esto es con GroovyShell.

GroovyShell shell = new GroovyShell()
def Util = shell.parse(new File('Util.groovy'))
def data = Util.fetchData()

6

Groovy no tiene una palabra clave de importación como los lenguajes de scripting típicos que harán una inclusión literal del contenido de otro archivo (aludido aquí: ¿Groovy proporciona un mecanismo de inclusión? ).
Debido a su naturaleza orientada a objetos / clases, tienes que "jugar" para que cosas como esta funcionen. Una posibilidad es hacer que todas sus funciones de utilidad sean estáticas (ya que dijo que no usan objetos) y luego realizar una importación estática en el contexto de su shell de ejecución. Entonces puede llamar a estos métodos como "funciones globales".
Otra posibilidad sería utilizar un objeto Binding ( http://groovy.codehaus.org/api/groovy/lang/Binding.html) mientras crea su Shell y vincula todas las funciones que desea a los métodos (la desventaja aquí sería tener que enumerar todos los métodos en el enlace, pero tal vez podría usar la reflexión). Otra solución más sería anular methodMissing(...)el objeto delegado asignado a su shell, lo que le permite básicamente realizar un envío dinámico utilizando un mapa o cualquier método que desee.

Varios de estos métodos se muestran aquí: http://www.nextinstruction.com/blog/2012/01/08/creating-dsls-with-groovy/ . Avísame si quieres ver un ejemplo de una técnica en particular.


7
este enlace ahora está muerto
Nicolas Mommaerts


5

Aquí hay un ejemplo completo de cómo incluir un script dentro de otro.
Simplemente ejecute el archivo Testmain.groovy
Comentarios explicativos incluidos porque soy bueno así;]

Testutils.groovy

// This is the 'include file'
// Testmain.groovy will load it as an implicit class
// Each method in here will become a method on the implicit class

def myUtilityMethod(String msg) {
    println "myUtilityMethod running with: ${msg}"
}

Testmain.groovy

// Run this file

// evaluate implicitly creates a class based on the filename specified
evaluate(new File("./Testutils.groovy"))
// Safer to use 'def' here as Groovy seems fussy about whether the filename (and therefore implicit class name) has a capital first letter
def tu = new Testutils()
tu.myUtilityMethod("hello world")

0

Para los recién llegados, parece que groovy ahora admite el :load file-pathcomando que simplemente redirige la entrada del archivo dado, por lo que ahora es trivial incluir scripts de biblioteca.

Funciona como entrada al groovysh y como una línea en un archivo cargado:
groovy:000> :load file1.groovy

file1.groovy puede contener:
:load path/to/another/file invoke_fn_from_file();


¿Puedes ampliar esto por favor? ¿Dónde está esto en los documentos? ¿Dónde pongo :load file-path?
Christoffer Hammarström

Bueno, funciona como entrada al groovysh y como una línea en un archivo cargado: <br/> groovy:000> :load file1.groovy file1.groovy puede contener: <br/>:load path/to/another/file
Jack Punt

1
Encontré load en los documentos . Si lo entiendo correctamente, ¿ solo funciona con groovysh?
Christoffer Hammarström

Sin embargo, esto no funcionará con la ruta definida dentro de una variable, ¿verdad?
user2173353

0

Una combinación de respuestas de @grahamparks y @snowindy con un par de modificaciones es lo que funcionó para mis scripts Groovy que se ejecutan en Tomcat:

Utils.groovy

class Utils {
    def doSth() {...}
}

MyScript.groovy:

/* import Utils --> This import does not work. The class is not even defined at this time */
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(new File("full_path_to/Utils.groovy")); // Otherwise it assumes current dir is $CATALINA_HOME
def foo = groovyClass.newInstance(); // 'def' solves compile time errors!!
foo.doSth(); // Actually works!

Recibo: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

0

Groovy puede importar otras clases maravillosas exactamente como lo hace Java. Solo asegúrese de que la extensión del archivo de la biblioteca sea .groovy.

    $ cat lib/Lib.groovy
    package lib
    class Lib {
       static saySomething() { println 'something' }
       def sum(a,b) { a+b }
    }

    $ cat app.gvy
    import lib.Lib
    Lib.saySomething();
    println new Lib().sum(37,5)

    $ groovy app
    something
    42

-1

Después de algunas investigaciones, he llegado a la conclusión de que el siguiente enfoque parece el mejor.

algunos / subpackage / Util.groovy

@GrabResolver(name = 'nexus', root = 'https://local-nexus-server:8443/repository/maven-public', m2Compatible = true)
@Grab('com.google.errorprone:error_prone_annotations:2.1.3')
@Grab('com.google.guava:guava:23.0')
@GrabExclude('com.google.errorprone:error_prone_annotations')

import com.google.common.base.Strings

class Util {
    void msg(int a, String b, Map c) {
        println 'Message printed by msg method inside Util.groovy'
        println "Print 5 asterisks using the Guava dependency ${Strings.repeat("*", 5)}"
        println "Arguments are a=$a, b=$b, c=$c"
    }
}

ejemplo.groovy

#!/usr/bin/env groovy
Class clazz = new GroovyClassLoader().parseClass("${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy" as File)
GroovyObject u = clazz.newInstance()
u.msg(1, 'b', [a: 'b', c: 'd'])

Para ejecutar el example.groovyscript, agréguelo a la ruta de su sistema y escriba desde cualquier directorio:

example.groovy

El guión imprime:

Message printed by msg method inside Util.groovy
Print 5 asterisks using the Guava dependency *****
Arguments are a=1, b=b, c=[a:b, c:d]

El ejemplo anterior se probó en el siguiente entorno: Groovy Version: 2.4.13 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Linux

El ejemplo demuestra lo siguiente:

  • Cómo usar una Utilclase dentro de un guión maravilloso.
  • Una Utilclase que llama a la Guavabiblioteca de terceros incluyéndola como una Grapedependencia ( @Grab('com.google.guava:guava:23.0')).
  • La Utilclase puede residir en un subdirectorio.
  • Pasar argumentos a un método dentro de la Utilclase.

Comentarios / sugerencias adicionales:

  • Utilice siempre una clase maravillosa en lugar de una secuencia de comandos maravillosa para la funcionalidad reutilizable dentro de sus secuencias de comandos maravillosas. El ejemplo anterior usa la clase Util definida en el archivo Util.groovy. El uso de scripts maravillosos para una funcionalidad reutilizable es problemático. Por ejemplo, si usa un script maravilloso, entonces la clase Util tendría que ser instanciada en la parte inferior del script con new Util(), pero lo más importante, tendría que colocarse en un archivo llamado cualquier cosa menos Util.groovy. Consulte Scripts versus clases para obtener más detalles sobre las diferencias entre los scripts maravillosos y las clases maravillosas.
  • En el ejemplo anterior utilizo la ruta en "${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy"lugar de "some/subpackage/Util.groovy". Esto garantizará que el Util.groovyarchivo siempre se encontrará en relación con la ubicación del script maravilloso ( example.groovy) y no con el directorio de trabajo actual. Por ejemplo, usar "some/subpackage/Util.groovy"daría como resultado una búsqueda en WORK_DIR/some/subpackage/Util.groovy.
  • Siga la convención de nomenclatura de clases de Java para nombrar sus maravillosos scripts. Personalmente, prefiero una pequeña desviación donde los guiones comienzan con una letra minúscula en lugar de mayúscula. Por ejemplo, myScript.groovyes un nombre de script y MyClass.groovyes un nombre de clase. La asignación de nombres my-script.groovydará lugar a errores de tiempo de ejecución en determinados escenarios porque la clase resultante no tendrá un nombre de clase Java válido.
  • En el mundo de JVM en general, la funcionalidad relevante se llama JSR 223: Scripting para Java . En groovy en particular, la funcionalidad se denomina mecanismos de integración Groovy . De hecho, se puede utilizar el mismo enfoque para llamar a cualquier lenguaje JVM desde Groovy o Java. Algunos ejemplos notables de estos lenguajes JVM son Groovy, Java, Scala, JRuby y JavaScript (Rhino).
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.