У тому числі груві-сценарій в іншому груві


97

Я читав, як просто імпортувати файл groovy в інший скрипт groovy

Я хочу визначити загальні функції в одному groovy-файлі та викликати ці функції з інших groovy-файлів.

Я розумію, що це використовуватиме Groovy як мову сценаріїв, тобто мені не потрібні класи / об'єкти. Я намагаюся зробити щось на зразок dsl, що можна зробити в groovy. Усі змінні будуть затверджені з Java, і я хочу виконати groovy скрипт в оболонці.

Чи можливо це взагалі? Хтось може навести якийсь приклад.


2
можливий дублікат сценарію Load з groovy script
tim_yates

Відповіді:


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

Помістіть це у верхній частині вашого сценарію. Це введе вміст файлу groovy (просто замініть ім'я файлу між подвійними лапками своїм groovy скриптом).

Я роблю це з класом напрочуд під назвою "Tools.groovy".


7
Ім'я файлу має відповідати правилам іменування класів Java, щоб це працювало.
willkil

2
Питання - Як я можу передати аргументи сценарію, який я оцінюю, використовуючи цей синтаксис?
Стів

3
@steve Ти не можеш, але ти можеш визначити функцію в цьому сценарії, яку ти викликаєш аргументами
Nilzor,

11
Це не працює ... Сценарій добре оцінений, але в області виклику не існує оголошень (def, клас тощо)
LoganMzz

3
Ви повинні повернути об'єкт з першого виклику, а потім призначити результат оцінки змінній.
LoganMzz

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

Інший спосіб зробити це - визначити функції в грувному класі та проаналізувати та додати файл до шляху до класу під час виконання:

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

2
Це рішення насправді найкраще працювало для мене. Коли я спробував використати прийняту відповідь, я отримав помилку, сказавши, що мій основний скрипт не зміг вирішити клас, визначений в оцінюваному скрипті. Для чого це варто ...
cBlaine

1
Я спробував кілька різних підходів, які були розміщені на SO, і тільки це спрацювало. Інші видавали помилки про те, що не можуть вирішити клас або методи. Це версія, яку я використовую, версія Groovy Версія: 2.2.2 JVM: 1.8.0 Виробник: Oracle Corporation ОС: Windows 7.
Kuberchaun

Це спрацювало чудово. Обов’язково використовуйте GroovyObjectявно, це не заповнювач для вашого власного імені класу.
перевіряв

1
Все-таки я отримую: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

Рятувальник життя. Дякую, товариш !!
Anjana Silva

30

Я думаю, що найкращим вибором є впорядкування корисних речей у формі грубих класів, додавання їх до classpath і нехай основний сценарій посилається на них за допомогою ключового слова імпорту.

Приклад:

скрипти / DbUtils.groovy

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

сценарії / script1.groovy:

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

запущений сценарій:

cd scripts
groovy -cp . script1.groovy

Цікаво, як би це працювало, якщо у вас є структура каталогів, як-от libта srcкаталоги
Gi0rgi0s

9

Я роблю це так GroovyShell.

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

6

Groovy не має ключового слова імпорту, як-от типових мов сценаріїв, які б буквально включали вміст іншого файлу (тут натякається: Чи передбачено 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
це посилання вже мертве
Nicolas Mommaerts

6

Як щодо ставлення до зовнішнього сценарію як до класу 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]

5

Ось повний приклад включення одного сценарію до іншого.
Просто запустіть файл 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 Hammarström

Ну, це працює як вхід до groovysh & як рядок у завантаженому файлі: <br/> groovy:000> :load file1.groovy file1.groovy може містити: <br/>:load path/to/another/file
Джек Пунт

1
Я знайшов навантаження в документації . Якщо я правильно зрозумів, він працює тільки з groovysh?
Крістофер Хаммарстрем

Це, однак, не буде працювати із шляхом, визначеним всередині змінної, чи не так?
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
докаспар

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

приклад.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 для багаторазового використання у ваших скриптах groovy. У наведеному вище прикладі використовується клас Util, визначений у файлі Util.groovy. Використання груві-скриптів для багаторазового використання є проблематичним. Наприклад, якщо використовувати groovy скрипт, то клас Util повинен був би бути створений у нижній частині скрипту new Util(), але найголовніше, щоб він був розміщений у файлі з іменем що-небудь, крім Util.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: Сценарій для Java . Зокрема, в groovy функціональність називається механізмами інтеграції Groovy . Насправді той самий підхід можна використовувати для виклику будь-якої мови JVM зсередини Groovy або Java. Деякі помітні приклади таких мов JVM - Groovy, Java, Scala, JRuby та JavaScript (Rhino).
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.