Groovy mengevaluasi String menggunakan DSL saat berada dalam penutupan Groovy

Ini mirip dengan Memerlukan bantuan sintaksis Groovy untuk menghasilkan Penutupan dari sebuah String tetapi sedikit lebih rumit:

Saya bekerja dengan DSL kompleks yang tidak saya kendalikan (khususnya Jenkins Job DSL, tetapi saya menggunakan DSL hipotetis untuk kejelasan) dan saya telah memecah kode saya menjadi penutupan yang dikembalikan. Semua penutupan yang tidak dimulai dengan "saya" dalam kode ini adalah DSL hipotetis:

//scriptContext is a reference to the original script as this is a util class
static def myMethod(def scriptContext) {
    //everything here comes from some hypothetical DSL
    scriptContext.diningTable() {
        ....
        fruits myCreateFruitsClosure()
        ....
    }
}    
static def myCreateFruitsClosure() {
   return {
       apple('Gala') {
           seed("brown")
           shape {
              funky()
              dimpled()
           }
       }
   }
}

Cara di atas berfungsi dengan baik.

Sekarang katakanlah saya ingin detail Apple ditentukan secara dinamis dan saya memuat beberapa kode asyik dari suatu tempat (misalnya, XML).

Sekarang saya punya:

// I pass in the script context for the binding
static def myCreateFruitsClosure(def scriptContext) {
   return {
       apple('Gala') {
           // This comes from somewhere in real life:
           def code = """seed("blue"); shape { oval() ; cardioid() }"""
           def evalWrapper = new GroovyShell(context.binding).evaluate(' { -> ' + code + '}')                      
           evalWrapper()
       }
   }
}

Dan saya mendapatkan "Tidak ada tanda tangan metode: Script1.seed()" Jelas, metode seed() memang ada ketika tidak menggunakan eval karena berfungsi sebelumnya, jadi konteks/cakupan saya salah.

Saya sudah mencoba:

  • Segala macam kombinasi Eval.me() dan Script.evaluate()
  • Untuk membuat metode dan menjalankannya baik di dalam maupun di luar evaluasi()
  • Untuk memindahkan konten "createFruitsClosure()" langsung ke "myMethod()"

Semua ini menghasilkan kesalahan yang sedikit berbeda. Apa cara yang benar?


person Akom    schedule 23.01.2017    source sumber


Jawaban (1)


Pada akhirnya Saya berhasil dengan:

  1. Beralih ke blok kode terbuka alih-alih penutupan
  2. Menggunakan referensi eksplisit ke "pemilik" dari kode dinamis, dan menyediakannya melalui Binding. Dalam contoh ini, "self", yang merupakan pemilik, adalah penutupan apel.

Inilah hasilnya:

static def myCreateFruitsClosure() {
    return {
        apple {
            def code = """
                L:{
                   self.seed("blue")
                   self.shape {
                       oval()
                       cardioid()
                   }
                }"""  
            // sometimes "self: delegate" is appropriate instead of this:
            new GroovyShell(new Binding([self: owner])).evaluate(code)
        }
    }
}

Saya rasa itu lebih sederhana daripada yang saya coba buat. Perhatikan "L:" untuk memaksa evaluasi() memperlakukan kode sebagai blok kode terbuka dan bukan penutupan.

Jika ada yang punya cara yang lebih baik, beri tahu saya, tetapi ini cukup berhasil.

person Akom    schedule 24.01.2017
comment
Astaga. Saya telah menghabiskan beberapa jam mencoba memikirkan hal ini dan berpikir mungkin saya menjadi gila. Terima kasih atas solusinya - ini sangat membantu! - person Kat; 02.05.2018