Bao gồm một kịch bản thú vị trong một trò chơi khác


97

Tôi đã đọc cách chỉ cần nhập một tệp Groovy trong một tập lệnh Groovy khác

Tôi muốn xác định các hàm phổ biến trong một tệp Groovy và gọi các hàm đó từ các tệp Groovy khác.

Tôi hiểu điều này sẽ được sử dụng Groovy giống như một ngôn ngữ kịch bản, tức là tôi không cần các lớp / đối tượng. Tôi đang cố gắng một cái gì đó giống như dsl có thể được thực hiện trong Groovy. Tất cả các biến sẽ được xác nhận từ Java và tôi muốn thực thi tập lệnh groovy trong một trình bao.

Cái này nó có hoàn toàn có thể xảy ra được không ? Ai đó có thể cung cấp một số ví dụ.


2
có thể có bản sao của Tải tập lệnh từ tập lệnh groovy
tim_yates

Câu trả lời:


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

Đặt nó ở đầu kịch bản của bạn. Điều đó sẽ mang lại nội dung của một tệp Groovy (chỉ cần thay thế tên tệp giữa dấu ngoặc kép bằng tập lệnh Groovy của bạn).

Tôi thực hiện điều này với một lớp học được gọi là "Tools.groovy" một cách đáng ngạc nhiên.


7
Tên tệp cần tuân thủ các quy tắc đặt tên lớp của Java để điều này hoạt động.
willkil

2
Câu hỏi - Làm cách nào để tôi có thể chuyển các đối số cho tập lệnh mà tôi đang đánh giá bằng cú pháp này?
Steve

3
@steve Bạn không có thể, nhưng bạn có thể định nghĩa một hàm trong kịch bản mà bạn gọi với các đối số
Nilzor

11
Nó không hoạt động ... kịch bản cũng được đánh giá nhưng không có tuyên bố tồn tại trong phạm vi người gọi (def, lớp, vv)
LoganMzz

3
Bạn phải trả về một đối tượng từ lệnh gọi một, sau đó gán kết quả đánh giá cho một biến.
LoganMzz

45

Kể từ Groovy 2.2, có thể khai báo một lớp tập lệnh cơ sở với @BaseScriptchú thích biến đổi AST mới .

Thí dụ:

tệp MainScript.groovy :

abstract class MainScript extends Script {
    def meaningOfLife = 42
}

tệp test.groovy :

import groovy.transform.BaseScript
@BaseScript MainScript mainScript

println "$meaningOfLife" //works as expected

1
Tôi tiếp tục nhận được "không thể phân giải lớp" khi sử dụng phương pháp này. Bạn sẽ khuyên tôi làm gì? Có cách nào để nhập các lớp tùy chỉnh vào một tập lệnh thú vị khác không?
droidnoob 14/02/17

38

Một cách khác để thực hiện việc này là xác định các hàm trong một lớp groovy và phân tích cú pháp và thêm tệp vào classpath trong thời gian chạy:

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

2
Giải pháp này thực sự làm việc tốt nhất cho tôi. Khi tôi thử sử dụng câu trả lời được chấp nhận, tôi gặp lỗi khi nói rằng tập lệnh Groovy chính của tôi không thể phân giải lớp được xác định trong tập lệnh được đánh giá. Vì những gì nó đáng giá ...
cBlaine

1
Tôi đã thử một số cách tiếp cận khác nhau đã được đăng trên SO và chỉ cách này hoạt động. Những người khác đưa ra lỗi về việc không thể giải quyết lớp hoặc các phương thức. Đây là phiên bản tôi đang sử dụng phiên bản Groovy Version: 2.2.2 JVM: 1.8.0 Nhà cung cấp: Oracle Corporation OS: Windows 7.
Kuberchaun

Điều này làm việc tuyệt vời. Hãy chắc chắn sử dụng GroovyObjectrõ ràng, đó không phải là trình giữ chỗ cho tên lớp của riêng bạn.
haventchecked

1
Tôi vẫn nhận được: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

Tiết kiệm cuộc sống. Cảm ơn bạn!!
Anjana Silva

30

Tôi nghĩ rằng lựa chọn tốt nhất là tổ chức những thứ tiện ích dưới dạng các lớp Groovy, thêm chúng vào classpath và để tập lệnh chính tham chiếu đến chúng thông qua từ khóa nhập.

Thí dụ:

scripts / DbUtils.groovy

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

scripts / script1.groovy:

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

đang chạy tập lệnh:

cd scripts
groovy -cp . script1.groovy

Tôi tự hỏi điều này sẽ hoạt động như thế nào nếu bạn có cấu trúc thư mục như with liband srcdirectory
Gi0rgi0s

9

Cách mà tôi làm điều này là với GroovyShell.

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

6

Groovy không có từ khóa nhập giống như các ngôn ngữ kịch bản thông thường sẽ bao gồm nội dung của tệp khác theo nghĩa đen (ám chỉ ở đây: Groovy có cung cấp cơ chế bao gồm không? ).
Vì bản chất hướng đối tượng / lớp của nó, bạn phải "chơi trò chơi" để làm cho những thứ như thế này hoạt động. Một khả năng là làm cho tất cả các hàm tiện ích của bạn ở trạng thái tĩnh (vì bạn đã nói rằng chúng không sử dụng các đối tượng) và sau đó thực hiện nhập tĩnh trong ngữ cảnh của trình bao đang thực thi của bạn. Sau đó, bạn có thể gọi các phương thức này như "hàm toàn cầu".
Một khả năng khác sẽ là sử dụng đối tượng Binding ( http://groovy.codehaus.org/api/groovy/lang/Binding.html) trong khi tạo Shell của bạn và liên kết tất cả các chức năng bạn muốn với các phương thức (nhược điểm ở đây là phải liệt kê tất cả các phương thức trong ràng buộc nhưng bạn có thể sử dụng phản chiếu). Tuy nhiên, một giải pháp khác sẽ là ghi đè methodMissing(...)đối tượng ủy quyền được gán cho trình bao của bạn, điều này cho phép bạn thực hiện điều phối động về cơ bản bằng cách sử dụng bản đồ hoặc bất kỳ phương pháp nào bạn muốn.

Một số phương pháp này được trình bày tại đây: http://www.nextinstruction.com/blog/2012/01/08/creating-dsls-with-groovy/ . Hãy cho tôi biết nếu bạn muốn xem ví dụ về một kỹ thuật cụ thể.


7
liên kết này bây giờ đã chết
Nicolas Mommaerts

6

Làm thế nào về việc coi tập lệnh bên ngoài như một lớp Java? Dựa trên bài viết này: https://www.jmdawson.net/blog/2014/08/18/using-functions-from-one-groovy-script-in-aosystem/

getThing.groovy Tập lệnh bên ngoài

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

printThing.groovy Tập lệnh chính

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

Kết quả

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

5

Đây là một ví dụ đầy đủ về việc bao gồm một tập lệnh này trong một tập lệnh khác.
Chỉ cần chạy tệp Testmain.groovy
Các nhận xét giải thích kèm theo vì tôi thích thế;]

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

Đối với những người đến muộn, có vẻ như Groovy hiện đã hỗ trợ :load file-pathlệnh chỉ đơn giản là chuyển hướng đầu vào từ tệp đã cho, vì vậy bây giờ việc bao gồm các tập lệnh thư viện trở nên đơn giản.

Nó hoạt động như đầu vào cho groovysh & như một dòng trong một tệp được tải:
groovy:000> :load file1.groovy

file1.groovy có thể chứa:
:load path/to/another/file invoke_fn_from_file();


Bạn có thể mở rộng về điều này được không Cái này ở đâu trong tài liệu? Tôi đặt ở :load file-pathđâu?
Christoffer Hammarström

Vâng, nó hoạt động như đầu vào cho groovysh & như một dòng trong một file được tải: <br/> groovy:000> :load file1.groovy file1.groovy có thể chứa: <br/>:load path/to/another/file
Jack Punt

1
Tôi tìm thấy tải trong tài liệu . Nếu tôi hiểu đúng, nó chỉ hoạt động với groovysh?
Christoffer Hammarström

Tuy nhiên, điều này sẽ không hoạt động với đường dẫn được xác định bên trong một biến, phải không?
user2173353

0

Sự kết hợp giữa câu trả lời @grahamparks và @snowindy với một vài sửa đổi là những gì đã hoạt động cho các tập lệnh Groovy của tôi chạy trên 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!

Tôi nhận được: java.lang.NoClassDefFoundError: groovy.lang.GroovyObject
dokaspar

0

Groovy có thể nhập các lớp Groovy khác giống hệt như Java. Chỉ cần đảm bảo phần mở rộng của tệp thư viện là .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

Sau một số cuộc điều tra, tôi đã đi đến kết luận rằng cách tiếp cận sau đây có vẻ là tốt nhất.

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

Để chạy example.groovytập lệnh, hãy thêm nó vào đường dẫn hệ thống của bạn và nhập từ bất kỳ thư mục nào:

example.groovy

Kịch bản in:

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]

Ví dụ trên đã được thử nghiệm trong môi trường sau: Groovy Version: 2.4.13 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Linux

Ví dụ minh họa điều sau:

  • Cách sử dụng một Utillớp bên trong một tập lệnh thú vị.
  • Một Utillớp gọi Guavathư viện của bên thứ ba bằng cách bao gồm nó dưới dạng Grapephụ thuộc ( @Grab('com.google.guava:guava:23.0')).
  • Các Utillớp có thể nằm trong một thư mục con.
  • Truyền đối số cho một phương thức trong Utillớp.

Nhận xét / đề xuất bổ sung:

  • Luôn sử dụng một lớp linh hoạt thay vì tập lệnh linh hoạt cho chức năng có thể tái sử dụng trong các tập lệnh linh hoạt của bạn. Ví dụ trên sử dụng lớp Util được định nghĩa trong tệp Util.groovy. Việc sử dụng các tập lệnh linh hoạt cho chức năng có thể tái sử dụng là một vấn đề. Ví dụ: nếu sử dụng một tập lệnh Groovy thì lớp Util sẽ phải được khởi tạo ở cuối tập lệnh với new Util(), nhưng quan trọng nhất là nó sẽ phải được đặt trong một tệp có tên bất kỳ thứ gì ngoại trừ Util.groovy. Tham khảo Scripts so với các lớp để biết thêm chi tiết về sự khác biệt giữa các tập lệnh Groovy và các lớp groovy.
  • Trong ví dụ trên, tôi sử dụng đường dẫn "${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy"thay vì "some/subpackage/Util.groovy". Điều này sẽ đảm bảo rằng Util.groovytệp sẽ luôn được tìm thấy liên quan đến vị trí của tập lệnh groovy ( example.groovy) chứ không phải thư mục làm việc hiện tại. Ví dụ: sử dụng "some/subpackage/Util.groovy"sẽ dẫn đến tìm kiếm tại WORK_DIR/some/subpackage/Util.groovy.
  • Tuân theo quy ước đặt tên lớp Java để đặt tên cho các tập lệnh khó hiểu của bạn. Cá nhân tôi thích một độ lệch nhỏ trong đó các chữ viết bắt đầu bằng một chữ cái thường thay vì viết hoa. Ví dụ, myScript.groovylà một tên tập lệnh và MyClass.groovylà một tên lớp. Việc đặt tên my-script.groovysẽ dẫn đến lỗi thời gian chạy trong một số trường hợp nhất định vì lớp kết quả sẽ không có tên lớp Java hợp lệ.
  • Trong thế giới JVM nói chung, chức năng liên quan được đặt tên là JSR 223: Scripting cho Java . Đặc biệt, chức năng được đặt tên là cơ chế tích hợp Groovy . Trên thực tế, cách tiếp cận tương tự có thể được sử dụng để gọi bất kỳ ngôn ngữ JVM nào từ bên trong Groovy hoặc Java. Một số ví dụ đáng chú ý về các ngôn ngữ JVM như vậy là Groovy, Java, Scala, JRuby và JavaScript (Rhino).
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.