Включение отличного сценария в другой отличный


97

Я читал, как просто импортировать файл groovy в другой сценарий groovy

Я хочу определить общие функции в одном файле groovy и вызывать эти функции из других файлов groovy.

Я понимаю, что это будет использовать Groovy как язык сценариев, т.е. мне не нужны классы / объекты. Я пытаюсь сделать что-то вроде dsl, которое можно сделать в Groovy. Все переменные будут утверждены из Java, и я хочу выполнить отличный сценарий в оболочке.

Это вообще возможно ? Может кто-нибудь привести пример.


Ответы:


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

Поместите это в начало вашего сценария. Это приведет к содержимому отличного файла (просто замените имя файла в двойных кавычках на свой отличный сценарий).

Я делаю это с помощью класса, неожиданно названного "Tools.groovy".


7
Чтобы это работало, имя файла должно соответствовать правилам именования классов Java.
willkil 08

2
Вопрос. Как я могу передать аргументы сценарию, который я оцениваю, используя этот синтаксис?
Стив

3
@steve Вы не можете, но вы можете определить функцию в том скрипте, который вы вызываете с аргументами
Nilzor

11
Это не работает ... сценарий хорошо оценен, но в области действия вызывающего объекта (def, class и т. Д.)
Нет объявления

3
Вы должны вернуть объект из первого вызова, а затем присвоить результат оценки переменной.
LoganMzz 05

45

Начиная с Groovy 2.2, можно объявить базовый класс скрипта с новой @BaseScriptаннотацией преобразования AST.

Пример:

файл MainScript.groovy :

abstract class MainScript extends Script {
    def meaningOfLife = 42
}

файл test.groovy :

import groovy.transform.BaseScript
@BaseScript MainScript mainScript

println "$meaningOfLife" //works as expected

1
При использовании этого метода я все время получаю «не удается разрешить класс». Что бы вы посоветовали мне сделать? Есть ли способ импортировать пользовательские классы в другой отличный скрипт?
droidnoob

38

Другой способ сделать это - определить функции в классе Groovy, проанализировать и добавить файл в путь к классам во время выполнения:

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

3
Это решение действительно сработало для меня лучше всего. Когда я попытался использовать принятый ответ, я получил сообщение об ошибке, в котором говорилось, что мой основной Groovy-скрипт не смог разрешить класс, определенный в оцененном скрипте. Для чего это стоит ...
cBlaine

1
Я пробовал несколько разных подходов, которые были опубликованы на SO, и только это сработало. Другие выдавали ошибки из-за невозможности разрешить класс или методы. Это версия, которую я использую. Версия Groovy: 2.2.2 JVM: 1.8.0 Производитель: Oracle Corporation ОС: Windows 7.
Kuberchaun

1
Это отлично сработало. Обязательно используйте GroovyObjectявно, это не заполнитель для вашего собственного имени класса.
haventchered

1
Тем не менее я получаю: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

Спасатель жизни. Спасибо тебе, друг!!
Анджана Сильва

30

Я думаю, что лучший выбор - организовать служебные вещи в виде отличных классов, добавить их в путь к классам и позволить основному скрипту обращаться к ним через ключевое слово import.

Пример:

скрипты / DbUtils.groovy

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

scripts / script1.groovy:

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

запущенный скрипт:

cd scripts
groovy -cp . script1.groovy

Интересно, как это будет работать, если у вас есть структура каталогов, такая как with libи srcкаталогов
Gi0rgi0s

9

Я делаю это с помощью GroovyShell.

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

7

В Groovy нет ключевого слова import, как в типичных языках сценариев, которые будут буквально включать содержимое другого файла (здесь упоминается: Предоставляет ли Groovy механизм включения? ).
Из-за его объектно-классовой природы вы должны «играть в игры», чтобы такие вещи работали. Одна из возможностей - сделать все ваши служебные функции статическими (поскольку вы сказали, что они не используют объекты), а затем выполнить статический импорт в контексте исполняемой оболочки. Затем вы можете вызывать эти методы как «глобальные функции».
Другой вариант - использовать объект Binding ( http://groovy.codehaus.org/api/groovy/lang/Binding.html) при создании оболочки и привязке всех функций, которые вы хотите, к методам (недостатком здесь будет необходимость перечисления всех методов в привязке, но вы, возможно, можете использовать отражение). Еще одно решение - переопределить methodMissing(...)объект делегата, назначенный вашей оболочке, который позволяет вам в основном выполнять динамическую отправку с использованием карты или любого другого метода, который вы хотите.

Некоторые из этих методов демонстрируются здесь: http://www.nextinstruction.com/blog/2012/01/08/creating-dsls-with-groovy/ . Дайте мне знать, если вы хотите увидеть пример конкретной техники.


7
эта ссылка теперь мертва
Николас Моммартс

7

Как насчет того, чтобы рассматривать внешний скрипт как класс Java? На основе этой статьи: https://www.jmdawson.net/blog/2014/08/18/using-functions-from-one-groovy-script-in-another/

getThing.groovy Внешний скрипт

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

printThing.groovy Основной скрипт

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

Результат

$ groovy printThing.groovy
[thing, thin2, thing3]

6

Вот полный пример включения одного скрипта в другой.
Просто запустите файл Testmain.groovy. Включены
пояснительные комментарии, потому что мне это нравится;]

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

Для опоздавших, похоже, теперь groovy поддерживают :load file-path команду, которая просто перенаправляет ввод из заданного файла, поэтому теперь включение скриптов библиотеки является тривиальным делом.

Он работает как ввод в groovysh и как строка в загруженном файле:
groovy:000> :load file1.groovy

file1.groovy может содержать:
:load path/to/another/file invoke_fn_from_file();


Не могли бы вы подробнее рассказать об этом? Где это в документации? Куда мне положить :load file-path?
Christoffer

Ну, он работает как ввод в groovysh и как строка в загруженном файле: <br/> groovy:000> :load file1.groovy file1.groovy может содержать: <br/>:load path/to/another/file
Джек

1
Я нашел загрузку в документации . Если я правильно понял, работает только с groovysh?
Christoffer

Но это не будет работать с путем, определенным внутри переменной, не так ли?
user2173353

0

Комбинация ответов @grahamparks и @snowindy с парой модификаций - вот что сработало для моих скриптов Groovy, работающих на 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!

Я получаю: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

0

Groovy может импортировать другие классы Groovy точно так же, как это делает Java. Только убедитесь, что файл библиотеки имеет расширение .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

После некоторого исследования я пришел к выводу, что следующий подход кажется лучшим.

некоторые / подпакет / 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'])

Чтобы запустить example.groovyсценарий, добавьте его в системный путь и введите из любого каталога:

example.groovy

Скрипт напечатает:

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]

Приведенный выше пример был протестирован в следующей среде: Groovy Version: 2.4.13 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Linux

Пример демонстрирует следующее:

  • Как использовать Util класс внутри отличного скрипта.
  • UtilКласс вызова Guavaбиблиотеки третьей стороны, включив его в качестве Grapeзависимости ( @Grab('com.google.guava:guava:23.0')).
  • UtilКласс может находиться в подкаталоге.
  • Передача аргументов методу внутри Utilкласса.

Дополнительные комментарии / предложения:

  • Всегда используйте класс Groovy вместо Groovy script для многоразовой функциональности в ваших Groovy скриптах. В приведенном выше примере используется класс Util, определенный в файле Util.groovy. Использование Groovy-скриптов для многократного использования проблематично. Например, при использовании отличного сценария класс Util должен быть создан в нижней части сценария new Util(), но, что наиболее важно, он должен быть помещен в файл с именем, отличным от Util.groovy. Обратитесь к Сценариям и классам для получения дополнительных сведений о различиях между Groovy-скриптами и Groovy-классами.
  • В приведенном выше примере я использую путь "${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy"вместо "some/subpackage/Util.groovy". Это гарантирует, что Util.groovyфайл всегда будет найден относительно местоположения Groovy-скрипта ( example.groovy), а не текущего рабочего каталога. Например, использование "some/subpackage/Util.groovy"приведет к поиску в WORK_DIR/some/subpackage/Util.groovy.
  • Следуйте соглашению об именах классов Java, чтобы назвать свои отличные скрипты. Я лично предпочитаю небольшое отклонение, когда скрипты начинаются с нижней буквы, а не с заглавной. Например, myScript.groovyэто имя сценария и имя MyClass.groovyкласса. Присвоение имен my-script.groovyприведет к ошибкам времени выполнения в определенных сценариях, поскольку полученный класс не будет иметь допустимого имени класса Java.
  • В мире JVM соответствующая функциональность называется JSR 223: Scripting for the Java . В частности, в Groovy эта функция называется механизмами интеграции Groovy . Фактически, тот же подход можно использовать для вызова любого языка JVM из Groovy или Java. Некоторыми известными примерами таких языков JVM являются Groovy, Java, Scala, JRuby и JavaScript (Rhino).
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.