Einfügen eines groovigen Skripts in ein anderes grooviges

97

Ich habe gelesen, wie man einfach eine groovige Datei in ein anderes grooviges Skript importiert

Ich möchte allgemeine Funktionen in einer Groovy-Datei definieren und diese Funktionen aus anderen Groovy-Dateien aufrufen.

Ich verstehe, dass dies Groovy wie eine Skriptsprache verwenden würde, dh ich brauche keine Klassen / Objekte. Ich versuche so etwas wie dsl, das in groovy gemacht werden kann. Alle Variablen werden von Java aus aktiviert und ich möchte ein grooviges Skript in einer Shell ausführen.

Ist das überhaupt möglich? Kann jemand ein Beispiel geben.

Kannan Ekanath
quelle
2
Mögliches Duplikat des Load-Skripts aus dem groovigen Skript
tim_yates

Antworten:

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

Fügen Sie das oben in Ihr Skript ein. Dadurch wird der Inhalt einer groovigen Datei eingefügt (ersetzen Sie einfach den Dateinamen zwischen den doppelten Anführungszeichen durch Ihr grooviges Skript).

Ich mache das mit einer Klasse namens "Tools.groovy".

jmq
quelle
7
Der Dateiname muss den Klassennamenregeln von Java entsprechen, damit dies funktioniert.
Willkil
2
Frage - Wie kann ich mit dieser Syntax Argumente an das Skript übergeben, das ich auswerte?
Steve
3
@steve Sie können nicht, aber Sie können eine Funktion in diesem Skript definieren, die Sie mit Argumenten
aufrufen
11
Es funktioniert nicht ... Skript ist gut ausgewertet, aber es gibt keine Deklaration im Aufruferbereich (def, class, etc.)
LoganMzz
3
Sie müssen ein Objekt aus dem ersten Aufruf zurückgeben und dann einer Variablen das Bewertungsergebnis zuweisen.
LoganMzz
45

Ab Groovy 2.2 ist es möglich, eine Basisskriptklasse mit der neuen @BaseScriptAST-Transformationsanmerkung zu deklarieren .

Beispiel:

Datei MainScript.groovy :

abstract class MainScript extends Script {
    def meaningOfLife = 42
}

Datei test.groovy :

import groovy.transform.BaseScript
@BaseScript MainScript mainScript

println "$meaningOfLife" //works as expected
emesx
quelle
1
Ich bekomme immer wieder "Klasse kann nicht aufgelöst werden", wenn ich diese Methode verwende. Was würden Sie mir empfehlen? Gibt es eine Möglichkeit, benutzerdefinierte Klassen in ein anderes grooviges Skript zu importieren?
Droidnoob
38

Eine andere Möglichkeit, dies zu tun, besteht darin, die Funktionen in einer groovigen Klasse zu definieren und zu analysieren und die Datei zur Laufzeit dem Klassenpfad hinzuzufügen:

File sourceFile = new File("path_to_file.groovy");
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(sourceFile);
GroovyObject myObject = (GroovyObject) groovyClass.newInstance();
Grahamparks
quelle
2
Diese Lösung hat bei mir am besten funktioniert. Als ich versuchte, die akzeptierte Antwort zu verwenden, wurde die Fehlermeldung angezeigt, dass mein grooviges Hauptskript die im ausgewerteten Skript definierte Klasse nicht auflösen konnte. Für das, was es wert ist ...
cBlaine
1
Ich habe verschiedene Ansätze ausprobiert, die auf SO veröffentlicht wurden, und nur dies hat funktioniert. Die anderen warfen Fehler, weil sie die Klasse oder die Methoden nicht auflösen konnten. Dies ist die Version, die ich verwende Version Groovy Version: 2.2.2 JVM: 1.8.0 Hersteller: Oracle Corporation Betriebssystem: Windows 7.
Kuberchaun
Das hat super geklappt. Stellen Sie sicher, dass Sie dies GroovyObjectexplizit verwenden. Dies ist kein Platzhalter für Ihren eigenen Klassennamen.
Habe
1
Trotzdem bekomme ich: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar
Lebensretter. Danke, Mann!!
Anjana Silva
30

Ich denke, dass die beste Wahl darin besteht, Utility-Dinge in Form von groovigen Klassen zu organisieren, sie dem Klassenpfad hinzuzufügen und das Hauptskript über das Schlüsselwort import auf sie verweisen zu lassen.

Beispiel:

scripts / DbUtils.groovy

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

scripts / script1.groovy:

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

laufendes Skript:

cd scripts
groovy -cp . script1.groovy
Schneeindy
quelle
Ich frage mich, wie das funktionieren würde, wenn Sie eine Verzeichnisstruktur wie mit libund srcVerzeichnisse haben
Gi0rgi0s
9

Die Art und Weise, wie ich das mache, ist mit GroovyShell.

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

Groovy hat kein Import-Schlüsselwort wie typische Skriptsprachen, mit denen der Inhalt einer anderen Datei wörtlich eingeschlossen wird (hier erwähnt: Bietet groovy einen Einschlussmechanismus? ).
Aufgrund seiner objekt- / klassenorientierten Natur muss man "Spiele spielen", damit solche Dinge funktionieren. Eine Möglichkeit besteht darin, alle Dienstprogrammfunktionen statisch zu machen (da Sie sagten, dass sie keine Objekte verwenden) und dann einen statischen Import im Kontext Ihrer ausführenden Shell durchzuführen. Dann können Sie diese Methoden wie "globale Funktionen" aufrufen.
Eine andere Möglichkeit wäre die Verwendung eines Bindungsobjekts ( http://groovy.codehaus.org/api/groovy/lang/Binding.html)), während Sie Ihre Shell erstellen und alle gewünschten Funktionen an die Methoden binden (der Nachteil hier wäre, dass Sie alle Methoden in der Bindung auflisten müssten, aber Sie könnten möglicherweise Reflection verwenden). Eine weitere Lösung wäre das Überschreiben methodMissing(...)des Ihrer Shell zugewiesenen Delegatenobjekts, mit dem Sie den dynamischen Versand grundsätzlich mithilfe einer Karte oder einer beliebigen Methode durchführen können.

Einige dieser Methoden werden hier demonstriert: http://www.nextinstruction.com/blog/2012/01/08/creating-dsls-with-groovy/ . Lassen Sie mich wissen, wenn Sie ein Beispiel für eine bestimmte Technik sehen möchten.

Omnisis
quelle
7
Dieser Link ist jetzt tot
Nicolas Mommaerts
6

Wie wäre es, das externe Skript als Java-Klasse zu behandeln? Basierend auf diesem Artikel: https://www.jmdawson.net/blog/2014/08/18/using-functions-from-one-groovy-script-in-another/

getThing.groovy Das externe Skript

def getThingList() {
    return ["thing","thin2","thing3"]
}

printThing.groovy Das Hauptskript

thing = new getThing()  // new the class which represents the external script
println thing.getThingList()

Ergebnis

$ groovy printThing.groovy
[thing, thin2, thing3]
Askalee
quelle
5

Hier ist ein vollständiges Beispiel für das Einfügen eines Skripts in ein anderes.
Führen Sie einfach die Datei Testmain.groovy aus.
Erklärende Kommentare enthalten, weil ich so nett bin;]

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")
davidfrancis
quelle
0

Für Spätankömmlinge scheint es, dass groovy jetzt den :load file-pathBefehl unterstützt, der Eingaben aus der angegebenen Datei einfach umleitet. Daher ist es jetzt trivial, Bibliotheksskripte einzuschließen.

Es funktioniert als Eingabe für den Groovysh & als Zeile in einer geladenen Datei:
groovy:000> :load file1.groovy

file1.groovy kann enthalten:
:load path/to/another/file invoke_fn_from_file();

Jack Punt
quelle
Können Sie das bitte näher erläutern? Wo steht das in den Dokumenten? Wo lege ich hin :load file-path?
Christoffer Hammarström
Nun, es funktioniert als Eingabe für den Groovysh und als Zeile in einer geladenen Datei: <br/> groovy:000> :load file1.groovy file1.groovy kann enthalten: <br/>:load path/to/another/file
Jack Punt
1
Ich habe Last in den Dokumenten gefunden . Wenn ich richtig verstehe, funktioniert es nur mit groovysh?
Christoffer Hammarström
Dies funktioniert jedoch nicht mit dem in einer Variablen definierten Pfad, oder?
user2173353
0

Eine Kombination aus @ grahamparks- und @ snindindy-Antworten mit einigen Änderungen hat für meine auf Tomcat ausgeführten Groovy-Skripte funktioniert:

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!
Sergio Muriel
quelle
Ich bekomme: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar
0

Groovy kann andere Groovy-Klassen genau wie Java importieren. Stellen Sie nur sicher, dass die Erweiterung der Bibliotheksdatei .groovy lautet.

    $ 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
Meilen Zarathustra
quelle
-1

Nach einigen Untersuchungen bin ich zu dem Schluss gekommen, dass der folgende Ansatz der beste zu sein scheint.

some / 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"
    }
}

example.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'])

Um das example.groovySkript auszuführen , fügen Sie es Ihrem Systempfad hinzu und geben Sie es aus einem beliebigen Verzeichnis ein:

example.groovy

Das Skript druckt:

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]

Das obige Beispiel wurde in der folgenden Umgebung getestet: Groovy Version: 2.4.13 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Linux

Das Beispiel zeigt Folgendes:

  • So verwenden Sie eine UtilKlasse in einem groovigen Skript.
  • Eine UtilKlasse, die die GuavaBibliothek eines Drittanbieters aufruft, indem sie sie als GrapeAbhängigkeit ( @Grab('com.google.guava:guava:23.0')) einschließt .
  • Die UtilKlasse kann sich in einem Unterverzeichnis befinden.
  • Übergeben von Argumenten an eine Methode innerhalb der UtilKlasse.

Zusätzliche Kommentare / Vorschläge:

  • Verwenden Sie immer eine groovige Klasse anstelle eines groovigen Skripts, um wiederverwendbare Funktionen in Ihren groovigen Skripten zu erhalten. Im obigen Beispiel wird die in der Datei Util.groovy definierte Util-Klasse verwendet. Die Verwendung von groovigen Skripten für wiederverwendbare Funktionen ist problematisch. Wenn Sie beispielsweise ein grooviges Skript verwenden, muss die Util-Klasse am unteren Rand des Skripts mit instanziiert werden. new Util()Am wichtigsten ist jedoch, dass sie in einer Datei mit dem Namen "Util.groovy" abgelegt wird. Weitere Informationen zu den Unterschieden zwischen groovigen Skripten und groovigen Klassen finden Sie unter Skripte im Vergleich zu Klassen.
  • Im obigen Beispiel verwende ich den Pfad "${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy"anstelle von "some/subpackage/Util.groovy". Dadurch wird sichergestellt, dass die Util.groovyDatei immer in Bezug auf den Speicherort des groovigen Skripts ( example.groovy) und nicht auf das aktuelle Arbeitsverzeichnis gefunden wird. Zum Beispiel "some/subpackage/Util.groovy"würde die Verwendung zu suchen bei WORK_DIR/some/subpackage/Util.groovy.
  • Befolgen Sie die Namenskonvention für Java-Klassen, um Ihre groovigen Skripte zu benennen. Ich persönlich bevorzuge eine kleine Abweichung, bei der die Skripte mit einem Kleinbuchstaben anstelle eines Großbuchstabens beginnen. Ist beispielsweise myScript.groovyein Skriptname und MyClass.groovyein Klassenname. Das Benennen my-script.groovyführt in bestimmten Szenarien zu Laufzeitfehlern, da die resultierende Klasse keinen gültigen Java-Klassennamen hat.
  • In der JVM-Welt im Allgemeinen heißt die relevante Funktionalität JSR 223: Scripting für Java . Insbesondere bei Groovy wird die Funktionalität als Groovy-Integrationsmechanismen bezeichnet . Tatsächlich kann der gleiche Ansatz verwendet werden, um eine beliebige JVM-Sprache aus Groovy oder Java heraus aufzurufen . Einige bemerkenswerte Beispiele für solche JVM-Sprachen sind Groovy, Java, Scala, JRuby und JavaScript (Rhino).
Georgios F.
quelle