Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse a Closure into ConfigSlurper

Tags:

groovy

Background. I'm working on a gradle script and want to inline some default configuration using the ConfigSlurper. I'm already able to parse configurations from files, but can't seem to get the inlined configuration to work.

What i want to do is something like this:

myScript = {
   some {
     random {
        stuff = "You can access"
     }
   }
}

groovy.lang.Script script = new groovy.lang.Script() {
   @Override
   Object run() {
      theScript.call()
   }
}

ConfigSlurper slurper = new ConfigSlurper()
slurper.setBinding(["theScript": myScript])
ConfigObject parse = slurper.parse(script)
assert parse.some.random.stuff == "You can access"

This doesn't work. It says

Caught: groovy.lang.MissingMethodException: No signature of method: scriptStuff.some() is applicable for argument types: (scriptStuff$_run_closure1_closure2) values: [scriptStuff$_run_closure1_closure2@2aca5165]
Possible solutions: dump(), use([Ljava.lang.Object;), sleep(long), wait(), run(), run()
groovy.lang.MissingMethodException: No signature of method: scriptStuff.some() is applicable for argument types: (scriptStuff$_run_closure1_closure2) values: [scriptStuff$_run_closure1_closure2@2aca5165]
Possible solutions: dump(), use([Ljava.lang.Object;), sleep(long), wait(), run(), run()
    at scriptStuff$_run_closure1.doCall(scriptStuff.groovy:2)
    at scriptStuff$_run_closure1.doCall(scriptStuff.groovy)
    at scriptStuff$1.run(scriptStuff.groovy:12)
    at scriptStuff.run(scriptStuff.groovy:18)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

(scriptStuff refers to the file i run the pasted script from: scriptStuff.groovy)

So i'm not sure this in the correct direction. Actually it would be nicer if you could just pass the closure directly to the Slurper. But the Slurper only takes Script, script file, and String. Another direction would be to convert the clusure to a String. Is this possible?

Any solution would be nice.

Update Just wanted to elaborate a bit on my use case. I am creating a plugin to my gradle scripts. As a user i want to write something like this:

projectConfiguration {
   //this one is easy to parse with ConfigSlurper
   confFile = "some/path/to/configuration.groovy"

   //this is difficult to parse
   inlineConf = {
      some {
         inlined {
            configuration = "Cool!"
         }
      }
   }
}

So even though i could have parsed it using a string notation, then the notation above looks a lot slicker, and more like the rest of the gradle script. On the other hand i don't mind the code parsing the clojure beeing ugly. It's only user inside the plugin. The important ting is that the user facing part is clean.

like image 839
mathiasbn Avatar asked Mar 14 '13 11:03

mathiasbn


2 Answers

Set the delegate on your Closure to the script with a DELEGATE_FIRST resolve strategy:

class ClosureScript extends Script {
    Closure closure
    def run() {
        closure.resolveStrategy = Closure.DELEGATE_FIRST
        closure.delegate = this
        closure.call()
    }
}
script = new ClosureScript(closure: myScript)

See Creating a Groovy ConfigObject from a Closure.

like image 82
ataylor Avatar answered Oct 16 '22 02:10

ataylor


You can use ConfigSlurper to parse a String like this:

def myScript = '''
   some {
     random {
        stuff = "You can access"
     }
   }
'''
def config = new ConfigSlurper().parse(myScript)
assert config.some.random.stuff == "You can access"

If you really want to pass a Script, then you must extends Script because it is an abstract class. For example:

class MyConfiguration extends Script {  
    public Object run() { 
       some {
          random {
             stuff = "You can access"
          }
       }
    }
}

def config = new ConfigSlurper().parse(new MyConfiguration())
assert config.some.random.stuff=="You can access"
like image 1
jocki Avatar answered Oct 16 '22 00:10

jocki