Incluindo um script descolado em outro

97

Eu li como simplesmente importar um arquivo bacana em outro script bacana

Quero definir funções comuns em um arquivo bacana e chamar essas funções de outros arquivos bacanas.

Eu entendo que isso seria usar o Groovy como uma linguagem de script, ou seja, eu não preciso de classes / objetos. Estou tentando algo como DSL que pode ser feito no groovy. Todas as variáveis ​​serão declaradas do Java e eu quero executar um script groovy em um shell.

Isso é possível em tudo ? Alguém pode dar algum exemplo.

Kannan Ekanath
fonte
2
possível duplicata do script Load do script groovy
tim_yates

Respostas:

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

Coloque isso no topo do seu script. Isso trará o conteúdo de um arquivo bacana (apenas substitua o nome do arquivo entre as aspas pelo seu script bacana).

Eu faço isso com uma classe surpreendentemente chamada "Tools.groovy".

jmq
fonte
7
O nome do arquivo precisa estar em conformidade com as regras de nomenclatura de classe do Java para que isso funcione.
willkil
2
Pergunta - Como posso passar argumentos para o script que estou avaliando usando esta sintaxe?
Steve
3
@steve Você não pode, mas pode definir uma função nesse script que você chama com argumentos
Nilzor
11
Não funciona ... o script é bem avaliado, mas não existe nenhuma declaração no escopo do chamador (def, classe, etc.)
LoganMzz
3
Você deve retornar um objeto da chamada um e atribuir o resultado da avaliação a uma variável.
LoganMzz
45

A partir do Groovy 2.2, é possível declarar uma classe de script base com a nova @BaseScriptanotação de transformação AST.

Exemplo:

arquivo MainScript.groovy :

abstract class MainScript extends Script {
    def meaningOfLife = 42
}

arquivo test.groovy :

import groovy.transform.BaseScript
@BaseScript MainScript mainScript

println "$meaningOfLife" //works as expected
emesx
fonte
1
Eu continuo recebendo "incapaz de resolver a classe" ao usar esse método. O que você recomendaria que eu fizesse? Existe alguma maneira de importar classes personalizadas para outro script bacana?
droidnoob
38

Outra maneira de fazer isso é definir as funções em uma classe bacana, analisar e adicionar o arquivo ao classpath em tempo de execução:

File sourceFile = new File("path_to_file.groovy");
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(sourceFile);
GroovyObject myObject = (GroovyObject) groovyClass.newInstance();
Grahamparks
fonte
2
Essa solução realmente funcionou melhor para mim. Quando tentei usar a resposta aceita, recebi um erro dizendo que meu script principal do groovy não foi capaz de resolver a classe definida no script avaliado. Pelo que vale a pena ...
cBlaine
1
Tentei várias abordagens diferentes que foram postadas no SO e apenas isso funcionou. Os outros lançaram erros por não serem capazes de resolver a aula ou os métodos. Esta é a versão que estou usando, versão Groovy Versão: 2.2.2 JVM: 1.8.0 Fornecedor: Oracle Corporation OS: Windows 7.
Kuberchaun
Isso funcionou muito bem. Certifique-se de usar GroovyObjectexplicitamente, isso não é um espaço reservado para o seu próprio nome de classe.
verificado em
1
Ainda recebo: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar
Salva-vidas. Obrigada companheiro !!
Anjana Silva
30

Eu acho que a melhor escolha é organizar coisas utilitárias na forma de classes bacanas, adicioná-las ao classpath e deixar o script principal se referir a elas por meio da palavra-chave import.

Exemplo:

scripts / DbUtils.groovy

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

scripts / script1.groovy:

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

executando script:

cd scripts
groovy -cp . script1.groovy
nevado
fonte
Gostaria de saber como isso funcionaria se você tivesse uma estrutura de diretório como com libe srcdiretórios
Gi0rgi0s
9

A maneira que faço isso é com GroovyShell.

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

O Groovy não tem uma palavra-chave de importação como as linguagens de script típicas que farão uma inclusão literal do conteúdo de outro arquivo (aludido aqui: O groovy fornece um mecanismo de inclusão? ).
Por causa de sua natureza orientada a objeto / classe, você precisa "brincar" para fazer coisas como essa funcionarem. Uma possibilidade é tornar todas as funções do seu utilitário estáticas (já que você disse que elas não usam objetos) e, em seguida, executar uma importação estática no contexto do shell em execução. Em seguida, você pode chamar esses métodos como "funções globais".
Outra possibilidade seria usar um objeto Binding ( http://groovy.codehaus.org/api/groovy/lang/Binding.html) ao criar seu Shell e vincular todas as funções desejadas aos métodos (a desvantagem aqui seria ter de enumerar todos os métodos na vinculação, mas talvez você pudesse usar reflexão). Outra solução seria sobrescrever methodMissing(...)no objeto delegado atribuído ao seu shell, o que permite basicamente fazer o despacho dinâmico usando um mapa ou qualquer método que desejar.

Vários desses métodos são demonstrados aqui: http://www.nextinstruction.com/blog/2012/01/08/creating-dsls-with-groovy/ . Deixe-me saber se você deseja ver um exemplo de uma técnica específica.

omnisis
fonte
7
este link está morto
Nicolas Mommaerts
5

Aqui está um exemplo completo de inclusão de um script dentro de outro.
Basta executar o arquivo Testmain.groovy.
Comentários explicativos incluídos porque sou legal assim;]

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
fonte
0

Para os que chegaram mais tarde, parece que o groovy agora suporta o :load file-pathcomando que simplesmente redireciona a entrada do arquivo fornecido, então agora é trivial incluir scripts de biblioteca.

Funciona como entrada para o groovysh e como uma linha em um arquivo carregado:
groovy:000> :load file1.groovy

file1.groovy pode conter:
:load path/to/another/file invoke_fn_from_file();

Jack Punt
fonte
Você pode expandir isso, por favor? Onde fica isso nos documentos? Onde eu coloco :load file-path?
Christoffer Hammarström
Bem, funciona como entrada para o groovysh & como uma linha em um arquivo carregado: <br/> groovy:000> :load file1.groovy file1.groovy pode conter: <br/>:load path/to/another/file
Jack Punt
1
Eu encontrei carga nos documentos . Se bem entendi, ele funciona com groovysh?
Christoffer Hammarström
No entanto, isso não funcionará com o caminho definido dentro de uma variável, certo?
user2173353
0

Uma combinação de respostas @grahamparks e @snowindy com algumas modificações é o que funcionou para meus scripts Groovy em execução no 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!
Sergio Muriel
fonte
Eu recebo: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar
0

O Groovy pode importar outras classes incríveis exatamente como o Java faz. Apenas certifique-se de que a extensão do arquivo de biblioteca é .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
milhas zaratustra
fonte
-1

Após alguma investigação, cheguei à conclusão de que a abordagem a seguir parece a melhor.

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

exemplo.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 executar o example.groovyscript, adicione-o ao caminho do sistema e digite em qualquer diretório:

example.groovy

O script 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]

O exemplo acima foi testado no seguinte ambiente: Groovy Version: 2.4.13 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Linux

O exemplo demonstra o seguinte:

  • Como usar uma Utilclasse dentro de um script bacana.
  • Uma Utilclasse que chama a Guavabiblioteca de terceiros incluindo-a como uma Grapedependência ( @Grab('com.google.guava:guava:23.0')).
  • A Utilclasse pode residir em um subdiretório.
  • Passando argumentos para um método dentro da Utilclasse.

Comentários / sugestões adicionais:

  • Sempre use uma classe bacana em vez de um script bacana para funcionalidade reutilizável em seus scripts bacanas. O exemplo acima usa a classe Util definida no arquivo Util.groovy. Usar scripts bacanas para funcionalidade reutilizável é problemático. Por exemplo, se estiver usando um script bacana, a classe Util teria que ser instanciada na parte inferior do script com new Util(), mas o mais importante, ela teria que ser colocada em um arquivo denominado qualquer coisa menos Util.groovy. Consulte Scripts versus classes para obter mais detalhes sobre as diferenças entre scripts e classes excelentes.
  • No exemplo acima, uso o caminho em "${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy"vez de "some/subpackage/Util.groovy". Isso garantirá que o Util.groovyarquivo sempre será encontrado em relação à localização do script groovy ( example.groovy) e não ao diretório de trabalho atual. Por exemplo, usar "some/subpackage/Util.groovy"resultaria na pesquisa em WORK_DIR/some/subpackage/Util.groovy.
  • Siga a convenção de nomenclatura de classe Java para nomear seus scripts groovy. Eu pessoalmente prefiro um pequeno desvio em que os scripts começam com uma letra minúscula em vez de maiúscula. Por exemplo, myScript.groovyé um nome de script e MyClass.groovyé um nome de classe. A nomenclatura my-script.groovyresultará em erros de tempo de execução em certos cenários porque a classe resultante não terá um nome de classe Java válido.
  • No mundo JVM em geral, a funcionalidade relevante é chamada JSR 223: Scripting for the Java . No groovy em particular, a funcionalidade é chamada de mecanismos de integração do Groovy . Na verdade, a mesma abordagem pode ser usada para chamar qualquer linguagem JVM de Groovy ou Java. Alguns exemplos notáveis ​​de tais linguagens JVM são Groovy, Java, Scala, JRuby e JavaScript (Rhino).
Georgios F.
fonte